Nasm中文手册二
第三章 NASM语言
----------------
3.1 NASM源程序行的组成。
就像很多其他的汇编器,每一行NASM源代码包含(除非它是一个宏,一个预处理操作
符,或一个汇编器操作符,参况第4,5章)下面四个部分的全部或某几个部分:
label: instruction operands ; comment
通常,这些域的大部分是可选的;label,instruction,comment存在或不存在都是允
许的。当然,operands域会因为instruction域的要求而必需存或必须不存在。
NASM使用反斜线(/)作为续行符;如果一个以一个反斜线结束,那第二行会被认为
是前面一行的一部分。
NASM对于一行中的空格符并没有严格的限制:labels可以在它们的前面有空格,或
其他任何东西。label后面的冒号同样也是可选的。(注意到,这意味着如果你想
要写一行'lodsb',但却错误地写成了'lodab',这仍将是有效的一行,但这一行不做
任何事情,只是定义了一个label。运行NASM时带上命令行选项'-w+orphan-labels'
会让NASM在你定义了一个不以冒号结尾的label时警告你。
labels中的有效的字符是字母,数字,'-','$','#','@','~','.'和'?'。但只有字母
'.',(具有特殊含义,参阅3.9),'_'和'?'可以作为标识符的开头。一个标识符还可
以加上一个'$'前缀,以表明它被作为一个标识符而不是保留字来处理。这样的话,
如果你想到链接进来的其他模块中定义了一个符号叫'eax',你可以用'$eax'在
NASM代码中引用它,以和寄存器的符号区分开。
instruction域可以包含任何机器指令:Pentium和P6指令,FPU指令,MMX指令还有甚
至没有公开的指令也会被支持。这些指令可以加上前缀'LOCK','REP','REPE/REPZ'
或'REPNE'/'REPNZ',通常,支持显示的地址尺寸和操作数尺寸前缀'A16','A32',
'O16'和'O32'。关于使用它们的一个例子在第九章给出。你也可以使用段寄存器
名作为指令前缀: 代码'es mov [bx],ax'等效于代码'mov [es:bx],ax'。我们推荐
后一种语法。因为它和语法中的其它语法特性一致。但是对于象'LODSB'这样的
指令,它没有操作数,但还是可以有一个段前缀, 对于'es lodsb'没有清晰地语法
处理方式
在使用一个前缀时,指令不是必须的,像'CS','A32','LOCK'或'REPE'这样的段前缀
可以单独出现在一行上,NASM仅仅产生一个前缀字节。
作为对实际机器指令的扩展,NASM同时提供了一定数量的伪操作指令,这在3.2节
详细描述。
指令操作数可以使用一定的格式:它们可以是寄存器,仅仅以寄存器名来表示(比
如:'ax','bp','ebx','cr0':NASM不使用'gas'的语法风格,在这种风格中,寄存器名
前必须加上一个'%'符号),或者它们可以是有效的地址(参阅3.3),常数(3.4),或
表达式。
对于浮点指令,NASM接受各种语法:你可以使用MASM支持的双操作数形式,或者你
可以使用NASM的在大多数情况下全用的单操作数形式。支持的所以指令的语法
细节可以参阅附录B。比如,你可以写:
fadd st1 ; this sets st0 := st0 + st1
fadd st0,st1 ; so does this
fadd st1,st0 ; this sets st1 := st1 + st0
fadd to st1 ; so does this
几乎所有的浮点指令在引用内存时必须使用以下前缀中的一个'DWORD',QWORD'
或'TWORD'来指明它所引用的内存的尺寸。
3.2 伪指令。
伪指令是一些并不是真正的x86机器指令,但还是被用在了instruction域中的指
令,因为使用它们可以带来很大的方便。当前的伪指令有'DB','DW','DD','DQ'和
‘DT’,它们对应的未初始化指令是'RESB','RESW','RESD','RESQ'和'REST','INCBIN'
命令,'EQU'命令和'TIEMS'前缀。
3.2.1 `DB'一类的伪指令: 声明已初始化的数据。
在NASM中,`DB', `DW', `DD', `DQ'和`DT'经常被用来在输出文件中声明已初始化
的数据,你可以多种方式使用它们:
db 0x55 ; just the byte 0x55
db 0x55,0x56,0x57 ; three bytes in succession
db 'a',0x55 ; character constants are OK
db 'hello',13,10,'$' ; so are string constants
dw 0x1234 ; 0x34 0x12
dw 'a' ; 0x41 0x00 (it's just a number)
dw 'ab' ; 0x41 0x42 (character constant)
dw 'abc' ; 0x41 0x42 0x43 0x00 (string)
dd 0x12345678 ; 0x78 0x56 0x34 0x12
dd 1.234567e20 ; floating-point constant
dq 1.234567e20 ; double-precision float
dt 1.234567e20 ; extended-precision float
'DQ'和'DT'不接受数值常数或字符串常数作为操作数。
3.2.2 `RESB'类的伪指令: 声明未初始化的数据。
`RESB', `RESW', `RESD', `RESQ' and `REST'被设计用在模块的BSS段中:它们声明
未初始化的存储空间。每一个带有单个操作数,用来表明字节数,字数,或双字数
或其他的需要保留单位。就像在2.2.7中所描述的,NASM不支持MASM/TASM的扣留未
初始化空间的语法'DW ?'或类似的东西:现在我们所描述的正是NASM自己的方式。
'RESB'类伪指令的操作数是有严格的语法的,参阅3.8。
比如:
buffer: resb 64 ; reserve 64 bytes
wordvar: resw 1 ; reserve a word
realarray resq 10 ; array of ten reals
3.2.3 `INCBIN':包含其他二进制文件。
'INCBIN'是从老的Amiga汇编器DevPac中借过来的:它将一个二进制文件逐字逐句地
包含到输出文件中。这能很方便地在一个游戏可执行文件中包含中图像或声音数
据。它可以以下三种形式的任何一种使用:
incbin "file.dat" ; include the whole file
incbin "file.dat",1024 ; skip the first 1024 bytes
incbin "file.dat",1024,512 ; skip the first 1024, and
; actually include at most 512
3.2.4 `EQU': 定义常数。
'EQU'定义一个符号,代表一个常量值:当使用'EQU'时,源文件行上必须包含一个label。
'EQU'的行为就是把给出的label的名字定义成它的操作数(唯一)的值。定义是不可更
改的,比如:
message db 'hello, world'
msglen equ $-message
把'msglen'定义成了常量12。'msglen'不能再被重定义。这也不是一个预自理定义:
'msglen'的值只被计算一次,计算中使用到了'$'(参阅3.5)在此时的含义。注意
‘EQU’的操作数也是一个严格语法的表达式。(参阅3.8)
3.2.5 `TIMES': 重复指令或数据。
为了与绝大多数C编译器的Makefile保持兼容,该选项也可以被写成'-U'。
2.1.13 `-e'选项: 仅预处理。
NASM允许预处理器独立运行。使用'-e'选项(不需要参数)会导致NASM预处理输入
文件,展开所有的宏,去掉所有的注释和预处理操作符,然后把结果文件打印在标
准输出上(如果'-o'选项也被指定的话,会被存入一个文件)。
该选项不能被用在那些需要预处理器去计算与符号相关的表达式的程序中,所以
如下面的代码:
%assign tablesize ($-tablestart)
会在仅预处理模式中会出错。
2.1.14 `-a' 选项: 不需要预处理。
如果NASM被用作编译器的后台,那么假设编译器已经作完了预处理,并禁止NASM的预
处理功能显然是可以节约时间,加快编译速度。'-a'选项(不需要参数),会让NASM把
它强大的预处理器换成另一个什么也不做的预处理器。
2.1.15 `-On'选项: 指定多遍优化。
NASM在缺省状态下是一个两遍的汇编器。这意味着如果你有一个复杂的源文件需要
多于两遍的汇编。你必须告诉它。
使用'-O'选项,你可以告诉NASM执行多遍汇编。语法如下:
(*)'-O0'严格执行两遍优化,JMP和Jcc的处理和0.98版类似,除了向后跳的JMP是短跳
转,如果可能,立即数在它们的短格式没有被指定的情况下使用长格式。
(*)'-O1'严格执行两遍优化,但前向分支被汇编成保证能够到达的代码;可能产生比
'-O0'更大的代码,但在分支中的偏移地址没有指定的情况下汇编成功的机率更大,
(*)'-On' 多编优化,最小化分支的偏移,最小化带符号的立即数,当'strict'关键字
没有用的时候重载指定的大小(参阅3.7),如果2<=n<=3,会有5*n遍,而不是n遍。
注意这是一个大写的O,和小写的o是不同的,小写的o是指定输出文件的格式,可参阅
2.1.1
2.1.16 `-t'选项: 使用TASM兼容模式。
NASM有一个与Borlands的TASM之间的受限的兼容格式。如果使用了NASM的'-t'选项,
就会产生下列变化:
(*)本地符号的前缀由'.'改为'@@'
(*)TASM风格的以'@'开头的应答文件可以由命令行指定。这和NASM支持的'-@resp'
风格是不同的。
(*)扩号中的尺寸替换被支持。在TASM兼容模式中,方括号中的尺寸替换改变了操作
数的尺寸大小,方括号不再支持NASM语法的操作数地址。比如,'mov eax,[DWORD VAL]'
在TASM兼容语法中是合法的。但注意你失去了为指令替换缺省地址类型的能力。
(*)'%arg'预处理操作符被支持,它同TASM的ARG操作符相似。
(*) `%local'预处理操作符。
(*) `%stacksize'预处理操作符。
(*) 某些操作符的无前缀形式被支持。 (`arg', `elif',`else', `endif', `if',
`ifdef', `ifdifi', `ifndef', `include',`local')
(*) 还有很多...
需要更多的关于操作符的信息,请参阅4.9的TASM兼容预处理操作符指令。
2.1.17 `-w'选项: 使汇编警告信息有效或无效。
NASM可以在汇编过程中监视很多的情况,其中很多是值得反馈给用户的,但这些情况
还不足以构成严重错误以使NASM停止产生输出文件。这些情况被以类似错误的形式
报告给用户,但在报告信息的前面加上'warning'字样。警告信息不会阻止NASM产生
输出文件并向操作系统返回成功信息。
有些情况甚至还要宽松:他们仅仅是一些值得提供给用户的信息。所以,NASM支持'-w'
命令行选项。它以使特定类型的汇编警告信息输出有效或无效。这样的警告类型是
以命名来描述的,比如,'orphan-labels',你可以以下列的命令行选项让此类警告信息
得以输出:'-w+orphan-labels',或者以'-w-orphan-labels'让此类信息不能输出。
可禁止的警告信息类型有下列一些:
(*)`macro-params'包括以错误的参数个数调用多行的宏定义的警告。这类警告信息
缺省情况下是输出的,至于为什么你可能需要禁止它,请参阅4.3.1。
(*)`orphan-labels'包含源文件行中没有指令却定义了一个没有结尾分号的label的
警告。缺省状况下,NASM不输出此类警告。如果你需要它,请参阅3.1的例子。
(*) 'number-overflow'包含那些数值常数不符合32位格式警告信息(比如,你很容易打
了很多的F,错误产生了'0x7fffffffffff')。这种警告信息缺省状况下是打开的。
2.1.18 `-v'选项: 打印版本信息。
输入'NASM -v'会显示你正使用的NASM的版本号,还有它被编译的时间。
如果你要提交bug报告,你可能需要版本号。
2.1.19 `NASMENV'环境变量。
如果你定义了一个叫'NASMENV'的环境变量,程序会被把它认作是命令行选项附加的一
部分,它会在真正的命令行之前被处理。你可以通过在'NASMENV'中使用'-i'选项来定
义包含文件的标准搜索路径。
环境变量的值是通过空格符分隔的,所以值'-s ic:/nasmlib'会被看作两个单独的操
作。也正因为如此,意味着值'-dNAME='my name'不会象你预期的那样被处理, 因为它
会在空格符处被分开,NASM的命令行处理会被两个没有意义的字符串'-dNAME="my'和
'name"'给弄混。
为了解决这个问题,NASM为此提供了一个特性,如果你在'NASMENV'环境变量的第一个
字符处写上一个非减号字符,NASM就会把这个字符当作是选项的分隔符。所以把环
境变量设成'!-s!-ic:/nasmlib'跟'-s -ic:/nasmlib'没什么两样,但是
'!-dNAME="my name"就会正常工作了。
这个环境变量以前叫做'NASM',从版本0.98.32以后开始叫这个名字。