⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 nasm.txt

📁 masm is a important thing.
💻 TXT
📖 第 1 页 / 共 5 页
字号:
eax]'会带一个double-word的0偏移。而常用的形式,'[eax]'则不会带有偏移域。

当你希望在16位的代码中存取32位段中的数据时,上面所描述的形式是非常有用
的。关于这方面的更多信息,请参阅9.2。实际上,如果你要存取一个在已知偏
移地址处的数据,而这个地址又大于16位值,如果你不指定一个dword偏移,
NASM会让高位上的偏移值丢失。

类似的,NASM会把'[eax*2]'分裂成'[eax+eax]' ,因为这样可以让偏移域不存在
以此节省空间;实际上,它也把'[eax*2+offset]'分成'[eax+eax+offset]',你
可以使用‘NOSPLIT'关键字改变这种行为:`[nosplit eax*2]'会强制
`[eax*2+0]'按字面意思被处理。

  3.4 常数
 
NASM能理解四种不同类型的常数:数值,字符,字符串和浮点数。

  3.4.1 数值常数。
 
  一个数值常数就只是一个数值而已。NASM允许你以多种方式指定数值使用的
  进制,你可以以后缀'H','Q','B'来指定十六进制数,八进制数和二进制数,
  或者你可以用C风格的前缀'0x'表示十六进制数,或者以Borland Pascal风
  格的前缀'$'来表示十六进制数,注意,'$'前缀在标识符中具有双重职责
  (参阅3.1),所以一个以'$'作前缀的十六进制数值必须在'$'后紧跟数字,而
  不是字符。
 
  请看一些例子:

              mov     ax,100          ; decimal
              mov     ax,0a2h         ; hex
              mov     ax,$0a2         ; hex again: the 0 is required
              mov     ax,0xa2         ; hex yet again
              mov     ax,777q         ; octal
              mov     ax,10010011b    ; binary

  3.4.2 字符型常数。

一个字符常数最多由包含在双引号或单引号中的四个字符组成。引号的类型
与使用跟NASM其它地方没什么区别,但有一点,单引号中允许有双引号出现。

一个具有多个字符的字符常数会被little-endian order,如果你编写:

                mov eax,'abcd'

      产生的常数不会是`0x61626364',而是`0x64636261',所以你把常数存入内存
      的话,它会读成'abcd'而不是'dcba'。这也是奔腾的'CPUID'指令理解的字符常
      数形式(参阅B.4.34)

  3.4.3 字符串常数。
 
  字符串常数一般只被一些伪操作指令接受,比如'DB'类,还有'INCBIN'。

一个字符串常数和字符常数看上去很相像,但会长一些。它被处理成最大长
度的字符常数之间的连接。所以,以下两个语句是等价的:

            db    'hello'               ; string constant
            db    'h','e','l','l','o'   ; equivalent character constants

还有,下面的也是等价的:

            dd    'ninechars'           ; doubleword string constant
            dd    'nine','char','s'     ; becomes three doublewords
            db    'ninechars',0,0,0     ; and really looks like this

注意,如果作为'db'的操作数,类似'ab'的常数会被处理成字符串常量,因
为它作为字符常数的话,还不够短,因为,如果不这样,那'db 'ab'会跟
'db 'a''具有同样的效果,那是很愚蠢的。同样的,三字符或四字符常数会
在作为'dw'的操作数时被处理成字符串。

  3.4.4 浮点常量
 
  浮点常量只在作为'DD','DQ','DT'的操作数时被接受。它们以传统的形式表
  达:数值,然后一个句点,然后是可选的更多的数值,然后是选项'E'跟上
  一个指数。句点是强制必须有的,这样,NASM就可以把它们跟'dd 1'区分开,
  它只是声明一个整型常数,而'dd 1.0'声明一个浮点型常数。
 
一些例子:

            dd    1.2                     ; an easy one
            dq    1.e10                   ; 10,000,000,000
            dq    1.e+10                  ; synonymous with 1.e10
            dq    1.e-10                  ; 0.000 000 000 1
            dt    3.141592653589793238462 ; pi

NASM不能在编译时求浮点常数的值。这是因为NASM被设计为可移植的,尽管它
常产生x86处理器上的代码,汇编器本身却可以和ANSI C编译器一起运行在任
何系统上。所以,汇编器不能保证系统上总存在一个能处理Intel浮点数的浮
点单元。所以,NASM为了能够处理浮点运算,它必须含有它自己的一套完整
的浮点处理例程,它大大增加了汇编器的大小,却获得了并不多的好处。
 
  3.5 表达式
 
  NASM中的表达式语法跟C里的是非常相似的。
 
  NASM不能确定编译时在计算表达式时的整型数尺寸:因为NASM可以在64位系
  统上非常好的编译和运行,不要假设表达式总是在32位的寄存器中被计算的,
  所以要慎重地对待整型数溢出的情况。它并不总能正常的工作。NASM唯一能
  够保证的是:你至少拥有32位长度。

NASM在表达式中支持两个特殊的记号,即'$'和'$$',它们允许引用当前指令
的地址。'$'计算得到它本身所在源代码行的开始处的地址;所以你可以简
单地写这样的代码'jmp $'来表示无限循环。'$$'计算当前段开始处的地址,
所以你可以通过($-$$)找出你当前在段内的偏移。

NASM提供的运算符以运算优先级为序列举如下:

  3.5.1 `|': 位或运算符。
 
运算符'|'给出一个位级的或运算,所执行的操作与机器指令'or'是完全相
同的。位或是NASM中优先级最低的运算符。

  3.5.2 `^': 位异或运算符。

      `^' 提供位异或操作。

  3.5.3 `&': 位与运算符。

      `&' 提供位与运算。

  3.5.4 `<<' and `>>': 位移运算符。

      `<<' 提供位左移, 跟C中的实现一样,所以'5<<3'相当于把5乘上8。'>>'提
      供位右移。在NASM中,这样的位移总是无符号的,所以位移后,左侧总是以
      零填充,并不会有符号扩展。
     
  3.5.5 `+' and `-': 加与减运算符。
 
  '+'与'-'运算符提供完整的普通加减法功能。

  3.5.6 `*', `/', `//', `%'和`%%': 乘除法运算符。
 
  '*'是乘法运算符。'/'和'//'都是除法运算符,'/'是无符号除,'//'是带
  符号除。同样的,'%'和'%%'提供无符号与带符号的模运算。
 
  同ANSI C一样,NASM不保证对带符号模操作执行的操作的有效性。
 
  因为'%'符号也被宏预处理器使用,你必须保证不管是带符号还是无符号的
  模操作符都必须跟有空格。
 
  3.5.7 一元运算符: `+', `-', `~'和`SEG'

这些只作用于一个参数的一元运算符是NASM的表达式语法中优先级最高的。
'-'把它的操作数取反,'+'不作任何事情(它只是为了和'-'保持对称),
'~'对它的操作数取补码,而'SEG'提供它的操作数的段地址(在3.6中会有
详细解释)。

  3.6 `SEG'和`WRT'
 
  当写很大的16位程序时,必须把它分成很多段,这时,引用段内一个符号的
  地址的能力是非常有必要的,NASM提供了'SEG'操作符来实现这个功能。
 
  'SEG'操作符返回符号所在的首选段的段基址,即一个段基址,当符号的偏
  移地址以它为参考时,是有效的,所以,代码:

              mov     ax,seg symbol
              mov     es,ax
              mov     bx,symbol

总是在'ES:BX'中载入一个指向符号'symbol'的有效指针。

而事情往往可能比这还要复杂些:因为16位的段与组是可以相互重叠的,
你通常可能需要通过不同的段基址,而不是首选的段基址来引用一个符
号,NASM可以让你这样做,通过使用'WRT'关键字,你可以这样写:

              mov     ax,weird_seg        ; weird_seg is a segment base
              mov     es,ax
              mov     bx,symbol wrt weird_seg

会在'ES:BX'中载入一个不同的,但功能上却是相同的指向'symbol'的指
针。

通过使用'call segment:offset',NASM提供fall call(段内)和jump,这里
'segment'和'offset'都以立即数的形式出现。所以要调用一个远过程,你
可以如下编写代码:

              call    (seg procedure):procedure
              call    weird_seg:(procedure wrt weird_seg)

(上面的圆括号只是为了说明方便,实际使用中并不需要)


NASM支持形如'call far procedure'的语法,跟上面第一句是等价的。'jmp'
的工作方式跟'call'在这里完全相同。

在数据段中要声明一个指向数据元素的远指针,可以象下面这样写:

              dw      symbol, seg symbol

NASM没有提供更便利的写法,但你可以用宏自己建造一个。

  3.7 `STRICT': 约束优化。
 
  当在汇编时把优化器打开到2或更高级的时候(参阅2.1.15)。NASM会使用
  尺寸约束('BYTE','WORD','DWORD','QWORD',或'TWORD'),会给它们尽可
  能小的尺寸。关键字'STRICT'用来制约这种优化,强制一个特定的操作
  数为一个特定的尺寸。比如,当优化器打开,并在'BITS 16'模式下:
 
              push dword 33

      会被编码成 `66 6A 21',而

              push strict dword 33

会被编码成六个字节,带有一个完整的双字立即数`66 68 21 00 00 00'.

而当优化器关闭时,不管'STRICT'有没有使用,都会产生相同的代码。

  3.8 临界表达式。
 
  NASM的一个限制是它是一个两遍的汇编器;不像TASM和其它汇编器,它总是
  只做两遍汇编。所以它就不能处理那些非常复杂的需要三遍甚至更多遍汇编
  的源代码。
 
  第一遍汇编是用于确定所有的代码与数据的尺寸大小,这样的话,在第二遍
  产生代码的时候,就可以知道代码引用的所有符号地址。所以,有一件事
  NASM不能处理,那就是一段代码的尺寸依赖于另一个符号值,而这个符号又
  在这段代码的后面被声明。比如:

              times (label-$) db 0
            label:  db      'Where am I?'

'TIMES'的参数本来是可以合法得进行计算的,但NASM中不允许这样做,因为
它在第一次看到TIMES时的时候并不知道它的尺寸大小。它会拒绝这样的代码。

              times (label-$+1) db 0
              label:  db      'NOW where am I?'

在上面的代码中,TIMES的参数是错误的。

NASM使用一个叫做临界表达式的概念,以禁止上述的这些例子,临界表达式
被定义为一个表达式,它所需要的值在第一遍汇编时都是可计算的,所以,
该表达式所依赖的符号都是之前已经定义了的,'TIMES'前缀的参数就是一个
临界表达式;同样的原因,'RESB'类的伪指令的参数也是临界表达式。

临界表达式可能会出现下面这样的情况:


                       mov     ax,symbol1
       symbol1         equ     symbol2
       symbol2:

在第一遍的时候,NASM不能确定'symbol1'的值,因为'symbol1'被定义成等于
'symbols2',而这时,NASM还没有看到symbol2。所以在第二遍的时候,当它遇
上'mov ax,symbol1',它不能为它产生正确的代码,因为它还没有知道'symbol1'
的值。当到达下一行的时候,它又看到了'EQU',这时它可以确定symbol1的值
了,但这时已经太晚了。

NASM为了避免此类问题,把'EQU'右侧的表达式也定义为临界表达式,所以,
'symbol1'的定义在第一遍的时候就会被拒绝。

这里还有一个关于前向引用的问题:考虑下面的代码段:

              mov     eax,[ebx+offset]
      offset  equ     10

NASM在第一遍的时候,必须在不知道'offset'值的情况下计算指令
'mov eax,[ebx+offset]'的尺寸大小。它没有办法知道'offset'足够小,足以

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -