现在的位置: 首页 > 综合 > 正文

第五章: 汇编器指令

2014年02月10日 ⁄ 综合 ⁄ 共 5101字 ⁄ 字号 评论关闭

【转】http://blog.csdn.net/hitop0609/archive/2009/07/07/4329486.aspx

尽管NASM极力避免MASN和TASM中的那些庸肿复杂的东西,但还是不得不支持少
量的指令,这些指令在本章进行描述。

NASM的指令有两种类型:用户级指令和原始指令。一般地,每一条指令都有一
个用户级形式和原始形式。在大多数情况下,我们推荐用户使用有户级指令,
它们以宏的形式运行,并去调用原始形式的指令。

原始指令被包含在一个方括号中;用户级指令没有括号。

除了本章所描述的这些通用的指令,每一种目标文件格式为了控制文件格式
的一些特性,可以使用一些另外的指令。这些格式相关的指令在第六章中跟
相关的文件格式一起进行介绍。

  5.1 `BITS': 指定目标处理器模式。
 
  'BITS'指令指定NASM产生的代码是被设计运行在16位模式的处理器上还是运行
  在32位模式的处理器上。语法是'BITS 16'或'BITS 32'
 
  大多数情况下,你可能不需要显式地指定'BITS'。'aout','coff','elf'和
  'win32'目标文件格式都是被设计用在32位操作系统上的,它们会让NASM缺
  省选择32位模式。而'obj'目标文件格式允许你为每一个段指定'USE16'或
  'USE32',然后NASM就会按你的指定设定操作模式,所以多次使用'BITS'是
  没有必要的。
 
  最有可能使用'BITS'的场合是在一个纯二进制文件中使用32位代码;这是因
  为'bin'输出格式在作为DOS的'.COM'程序,DOS的'.SYS'设备驱动程序,或引
  导程序时,默认都是16位模式。
 
如果你仅仅是为了在16位的DOS程序中使用32位指令,你不必指定'BITS 32',
如果你这样做了,汇编器反而会产生错误的代码,因为这样它会产生运行在
16位模式下,却以32位平台为目标的代码。

当NASM在'BITS 16'状态下时,使用32位数据的指令可以加一个字节的前缀
0x66,要使用32位的地址,可以加上0x67前缀。在'BITS 32'状态下,相反的
情况成立,32位指令不需要前缀,而使用16位数据的指令需要0x66前缀,使
用16位地址的指令需要0x67前缀。

'BITS'指令拥有一个等效的原始形式:[BITS 16]和[BITS 32]。而用户级的
形式只是一个仅仅调用原始形式的宏。

  5.1.1 `USE16' & `USE32': BITS的别名。

'USE16'和'USE32'指令可以用来取代'BITS 16'和'BITS 32',这是为了和其他
汇编器保持兼容性。

  5.2 `SECTION'或`SEGMENT': 改变和定义段。

'SECTION'指令('SEGMENT'跟它完全等效)改变你正编写的代码将被汇编进的段。
在某些目标文件格式中,段的数量与名称是确定的;而在别一些格式中,用户
可以建立任意多的段。因此,如果你企图切换到一个不存在的段,'SECTION'有
时可能会给出错误信息,或者定义出一个新段,
    
      Unix的目标文件格式和'bin'目标文件格式,都支持标准的段'.text','.data'
      和'bss'段,与之不同的的,'obj'格式不能辩识上面的段名,并需要把段名开
      头的句点去掉。

  5.2.1 宏 `__SECT__'
 
  'SECTION'指令跟一般指令有所不同,的用户级形式跟它的原始形式在功能上有
  所不同,原始形式[SECTION xyz],简单地切换到给出的目标段。用户级形式,
  'SECTION xyz'先定义一个单行宏'__SECT__',定义为原始形式[SECTION],这正
  是要执行的指令,然后执行它。所以,用户级指令:

              SECTION .text

被展开成两行:

      %define __SECT__        [SECTION .text]
              [SECTION .text]

用户会发现在他们自己的宏中,这是非常有用的。比如,4.3.3中定义的宏
'writefile'以下面的更为精致的写法会更有用:

%macro  writefile 2+
    
              [section .data]
    
        %%str:        db      %2
        %%endstr:
    
              __SECT__
    
              mov     dx,%%str
              mov     cx,%%endstr-%%str
              mov     bx,%1
              mov     ah,0x40
              int     0x21
    
      %endmacro

这个形式的宏,一次传递一个用出输出的字符串,先用原始形式的'SECTION'切
换至临时的数据段,这样就不会破会宏'__SECT__'。然后它把它的字符串声明在
数据段中,然后调用'__SECT__'切换加用户先前所在的段。这样就可以避免先前
版本的'writefile'宏中的用来跳过数据的'JMP'指令,而且在一个更为复杂的格
式模型中也不会失败,用户可以把这个宏放在任何独立的代码段中进行汇编。

  5.3 `ABSOLUTE': 定义绝对labels。
 
  'ABSOLUTE'操作符可以被认为是'SECTION'的另一种形式:它会让接下来的代码不
  在任何的物理段中,而是在一个从给定地址开始的假想段中。在这种模式中,你
  唯一能使用的指令是'RESB'类指令。

      `ABSOLUTE'可以象下面这样使用:

      absolute 0x1A
    
          kbuf_chr    resw    1
          kbuf_free   resw    1
          kbuf        resw    16

这个例子描述了一个关于在段地址0x40处的PC BIOS数据域的段,上面的代码把
'kbuf_chr'定义在0x1A处,'kbuf_free'定义在地址0x1C处,'kbuf'定义在地址
0x1E。

就像'SECTION'一样,用户级的'ABSOLUTE'在执行时会重定义'__SECT__'宏。

'STRUC'和'ENDSTRUC'被定义成使用'ABSOLUTE'的宏(同时也使用了'__SECT__')

'ABSOLUTE'不一定需要带有一个绝对常量作为参数:它也可以带有一个表达式(
实际上是一个临界表达式,参阅3.8),表达式的值可以是在一个段中。比如,一
个TSR程序可以在用它重用它的设置代码所占的空间:

              org     100h               ; it's a .COM program
    
              jmp     setup              ; setup code comes last
    
              ; the resident part of the TSR goes here
      setup:
              ; now write the code that installs the TSR here
    
      absolute setup
    
      runtimevar1     resw    1
      runtimevar2     resd    20
    
      tsr_end:

这会在setup段的开始处定义一些变量,所以,在setup运行完后,它所占用的内存
空间可以被作为TSR的数据存储空莘而得到重用。符号'tsr_end'可以用来计算TSR
程序所需占用空间的大小。

  5.4 `EXTERN': 从其他的模块中导入符中。
 
  'EXTERN'跟MASM的操作符'EXTRN',C的关键字'extern'极其相似:它被用来声明一
  个符号,这个符号在当前模块中没有被定义,但被认为是定义在其他的模块中,但
  需要在当前模块中对它引用。不是所有的目标文件格式都支持外部变量的:'bin'文
  件格式就不行。
 
  'EXTERN'操作符可以带有任意多个参数,每一个都是一个符号名:

      extern  _printf
      extern  _sscanf,_fscanf

有些目标文件格式为'EXTERN'提供了额外的特性。在所有情况下,要使用这些额外
特性,必须在符号名后面加一个冒号,然后跟上目标文件格式相关的一些文字。比如
'obj'文件格式允许你声明一个以外部组'dgroup'为段基址一个变量,可以象下面这样
写:

      extern  _variable:wrt dgroup

原始形式的'EXTERN'跟用户级的形式有所不同,因为它只能带有一个参数:对于多个参
数的支持是在预处理器级上的特性。

你可以把同一个变量作为'EXTERN'声明多次:NASM会忽略掉第二次和后来声明的,只采
用第一个。但你不能象声明其他变量一样声明一个'EXTERN'变量。

  5.5 `GLOBAL': 把符号导出到其他模块中。
 
  'GLOBAL'是'EXTERN'的对立面:如果一个模块声明一个'EXTERN'的符号,然后引用它,
  然后为了防止链接错误,另外某一个模块必须确实定义了该符号,然后把它声明为
  'GLOBAL',有些汇编器使用名字'PUBLIC'。

'GLOBAL'操作符所作用的符号必须在'GLOBAL'之后进行定义。

'GLOBAL'使用跟'EXTERN'相同的语法,除了它所引用的符号必须在同一样模块中已经被
定义过了,比如:

      global _main
      _main:
              ; some code

就像'EXTERN'一样,'GLOBAL'允许目标格式文件通过冒号定义它们自己的扩展。比如
'elf'目标文件格式可以让你指定全局数据是函数或数据。

      global  hashlookup:function, hashtable:data

就象'EXTERN'一样,原始形式的'GLOBAL'跟用户级的形式不同,仅能一次带有一个参

  5.6 `COMMON': 定义通用数据域。
 
  'COMMON'操作符被用来声明通用变量。一个通用变量很象一个在非初始化数据段中定义
  的全局变量。所以:

      common  intvar  4

      功能上跟下面的代码相似:

      global  intvar
      section .bss
    
      intvar  resd    1

不同点是如果多于一个的模块定义了相同的通用变量,在链接时,这些通用变量会被
合并,然后,所有模块中的所有的对'intvar'的引用会指向同一片内存。

就角'GLOBAL'和'EXTERN','COMMON'支持目标文件特定的扩展。比如,'obj'文件格式
允许通用变量为NEAR或FAR,而'elf'格式允许你指定通用变量的对齐需要。

      common  commvar  4:near  ; works in OBJ
      common  intarray 100:4   ; works in ELF: 4 byte aligned

它的原始形式也只能带有一个参数。

  5.7 `CPU': 定义CPU相关。
 
  'CPU'指令限制只能运行特定CPU类型上的指令。

      选项如下:

      (*) `CPU 8086' 只汇编8086的指令集。

      (*) `CPU 186' 汇编80186及其以下的指令集。

      (*) `CPU 286' 汇编80286及其以下的指令集。

      (*) `CPU 386' 汇编80386及其以下的指令集。

      (*) `CPU 486' 486指令集。

      (*) `CPU 586' Pentium指令集。

      (*) `CPU PENTIUM' 同586。

      (*) `CPU 686' P6指令集。

      (*) `CPU PPRO' 同686

      (*) `CPU P2' 同686

      (*) `CPU P3' Pentium III and Katmai指令集。

      (*) `CPU KATMAI' 同P3

      (*) `CPU P4' Pentium 4 (Willamette)指令集

      (*) `CPU WILLAMETTE' 同P4

      (*) `CPU IA64' IA64 CPU (x86模式下)指令集

所有选项都是大小写不敏感的,在指定CPU或更低一级CPU上的所有指令都会
被选择。缺省情况下,所有指令都是可用的。

抱歉!评论已关闭.