第五章: 彙編器指令

【轉】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上的所有指令都會
被選擇。缺省情況下,所有指令都是可用的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章