複製鏈接
請複製以下鏈接發送給好友

nasm

鎖定
Netwide Assembler (簡稱 NASM)是一款基於x86架構的彙編反彙編軟件。它可以用來編寫16位(808680286等)、32位(IA-32)和64位(x86_64)的程序。 NASM被認為是Linux平台上最受歡迎的彙編工具之一。
NASM 最初是在朱利安·霍爾(Julian Hall)的協助下由西蒙·泰瑟姆(Simon Tatham)開發的 [2]  。 截至2016年,它被一個由Hans Peter Anvin領導的小團隊所維護。 [1]  它是一款基於簡化版BSD許可證(2-clause BSD License)的開放源代碼軟件。
中文名
NASM
外文名
Netwide Assembler
別    名
The Netwide Assembler
由    來
為可移植性與模塊化而設計
實    質
x86彙編軟件
源碼發佈情況
開源(通過BSD許可證發佈)
支持的操作系統
類UNIX系統(包括macOS)、OS/2、Windows、MS-DOS

nasm軟件簡介

NASM可以輸出包括通用對象文件格式COFF)、OMF(Relocatable Object Module Format,用於80x86系列處理器上)、a.out、可執行與可鏈接格式ELF)、Mach-O二進制文件(.bin,二進制磁盤映像,用於編譯操作系統)等多種二進制格式,而地址無關代碼僅支持ELF對象文件。 NASM也有自己的稱為RDOFF(Relocatable Dynamic Object File Format)的二進制格式。
輸出格式的廣泛性允許將程序重定向到任何x86架構的操作系統。 此外,NASM可以創建浮動二進制文件,它可用於寫入引導加載程序、只讀存儲器ROM)映像以及操作系統開發的各個方面。NASM可以作為交叉彙編程序在非x86架構(如PowerPCSPARC)上運行,儘管它不能生成這些架構的處理器可用的程序。
它還可以輸出純二進制文件。它支持Pentium、P6、MMX3DNow!SSESSE2指令集
NASM不是AT&T風格彙編(GNU彙編器採用的語法,主要用於UNIX及Linux編程領域)而是Intel風格彙編(主要用於DOS/Windows編程領域)的變體。它還避免了MASM和兼容彙編器使用的自動生成段覆蓋(以及相關的 ASSUME 指令)等功能。

nasm設計初衷

NASM當初被設計出來的想法是”comp.lang.asm.x86“(也可能是”alt.lang.asm“),從本質上講,是因為沒有一個好的免費的x86系例的彙編器可以使用,所以,必須有人來寫一個。
  • a86不錯,但不是免費的,而且你不可能得到32位代碼編寫的功能——除非你付費。它只使用在DOS上。
  • gas是免費的,而且在DOS下和UNIX下都可以使用,但是它是作為gcc的一個後台而設計的,並不是很好,gcc一直就提供給它絕對正確的代碼,所以它的錯誤檢測功能相當弱,還有就是對於任何一個想真正利用它寫點東西的人來講,它的語法簡直太可怕了,並且你無法在裏面寫正確的16位代碼。
  • as86是專門為Minix和Linux設計的,但看上去並沒有很多文檔可以參考。
  • MASM不是很好,並且相當貴,還且只能運行在DOS下。
  • TASM好一些,但卻極入與MASM保持兼容,這就意味着無數的偽操作碼和繁瑣的約定,並且它的語法本質上就是MASM的,伴隨着的就是一些自相矛盾和奇怪的東西。它也是相當貴的,並且只能運行在DOS下。
所以,只有NASM才能使您愉悦地編程。

nasm使用

nasm安裝

在DOS或Windows下安裝NASM
如果你拿到了NASM的DOS安裝包,”nasm<版本號>.zip“,把它解壓到它自己的目錄下,比如C:\NASM。該包中會包含有四個可執行文件:NASM可執行文件nasm.exe和nasmw.exe,還有NDISASM可執行文件ndisasm.exe和ndisasmw.exe。文件名以“.w”結尾的是Win32可執行格式,是運行在Windows 95或Windows NT的Intel處理器上的;另外的是16位的DOS可執行文件
NASM運行時需要的唯一文件就是它自己的可執行文件,所以可以拷貝nasm.exe和nasmw.exe的其中一個到你自己的路徑下,或者可以編寫一個autoexec.bat把nasm的路徑加到你的PATH(環境變量)中去。(如果你只安裝了Win32版本的,你可能希望把文件名改成nasm.exe。)
就這樣,NASM裝好了。你不需要為了運行nasm而讓nasm目錄一直存在。(除非你把它加到了你的'PATH'中,所以如果你需要節省空間,你可刪掉它,但是你可能需要保留文檔或測試程序。)
如果你下載了DOS版的源碼包,nasmXXXs.zip,那nasm目錄還會包含完整的NASM源代碼,你可以選擇一個Makefile來重新構造你的NASM版本。
注意源文件insnsa.c、insnsd.c、insnsi.h和insnsn.c是由standard.mac中的指令自動生成的,儘管NASM 0.98版發佈版中包含了這些產生的文件,你如果改動了insns.dat、standard.mac或者文件,可能需要重新構造他們,在將來的源碼發佈中有可能將不再包含這些文件,多平台兼容的Perl可以從www.cpan.org上得到。
在Unix(除Mac OS X)下安裝NASM
如果你得到了Unix下的NASM源碼包nasm-<版本號>.tar.gz,把它解壓壓到一個目錄,比如/usr/local/src。包被解壓後會創建自己的子目錄nasm-x.xx。
NASM是一個自動配置的安裝包:一旦你解壓了它,使用cd命令切換到它的目錄下,輸入./configure,該腳本會找到最好的C編譯器來構造NASM,並據此建立Makefile。一旦NASM被自動配置好後,你可以輸入make來構造nasm和ndisasm二進制文件,然後輸入make install把它們安裝到/usr/local/bin,並把man頁安裝到/usr/local/man/man1下的nasm.1和ndisasm.1。你可以給配置腳本一個--prefix選項來指定安裝目錄,或者也可以自己來安裝。
NASM還附帶一套處理RDOFF目標文件格式的實用程序,它們在rdoff子目錄下,你可以用make rdf來構造它們,並使用make rdf_install來安裝,如果你需要的話。
如果NASM在自動配置的時候失敗了,你還是可以使用文件Makefile.unx來編譯它們,把這個文件改名為Makefile,然後輸入make。在rdoff子目錄下同樣有一個Makefile.unx文件。
在Mac OS X下安裝NASM
Mac OS X下的nasm安裝頗為簡單,你可以通過brew軟件包管理工具來安裝。確保你安裝了brew,然後鍵入一下命令:
brew install nasm
NASM就會自動完成安裝(默認安裝路徑為/usr/local/bin),並且添加至PATH。

nasm運行NASM

NASM命令行語法
要彙編一個文件,你可以以下面的格式執行一個命令:
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的,那你應該使用
-f aout
(Linux的a.out系統很久以前就過時了,已非常少見。)
就像其它的Unix編譯器與彙編器,NASM在碰到錯誤以前是不輸出任何信息的,所以除了出錯信息你看不到任何其它信息。
  • -o選項:指定輸出文件的文件名。
NASM會為你的輸出文件選擇一個文件名;具體如何做取決於目標文件的格式,對於微軟的目標文件格式(obj和win32),它會去掉你的源文件名的”.asm“擴展名(或者其他任何你喜歡使用的擴展名,NASM並不關心具體是什麼),並替換上obj。對於Unix的目標文件格式(aout、coff、elf和as86)它會替換成”.o“,對於rdf,它會使用”.rdf“,還有為bin格式,它會簡單地去掉擴展名,所以myfile.asm會產生一個輸出文件myfile。
如果輸出文件已經存在,NASM會覆蓋它,除非它的文件名與輸入文件同名,在這種情況下,它會給出一個警告信息,並使用nasm.out作為輸出文件的文件名。在某些情況下,上述行為是不能接受的,所以,NASM提供了-o選項,它能讓你指定你的輸出文件的文件名,你使用-o後面緊跟你為輸出文件取的名字,中間可以加空格也可以不加。比如:
nasm -f bin program.asm -o program.com
nasm -f bin driver.asm -odriver.sys
請注意這是一個小寫的o,跟大寫字母O是不同的,大寫的是用來指定需要傳遞的選項的數目。
  • -f選項:指定輸出文件的格式。
如果你沒有對NASM使用-f選項,它會自己為你選擇一個輸出文件格式。在發佈的NASM版本中,缺省的輸出格式總是bin;如果你自己編譯你的NASM,你可以在編譯的時候重定義OF_DEFAULT來選擇你需要的缺省格式。就象-o、-f與輸出文件格式之間的空格也是可選的,因此-f elf和-felf都是合法的。
所有可使用的輸出文件格式的列表可以通過運行命令nasm -hf得到。
  • -l選項:產生列表文件
如果你對NASM使用了-l選項,後面跟一個文件名,NASM會為你產生一個源文件的列表文件,在裏面,地址和產生的代碼列在左邊,實際的源代碼(包括宏擴展,除了那些指定不需要在列表中擴展的宏)列在右邊,比如:
nasm -f elf myfile.asm -l myfile.lst
  • -M選項:產生Makefile依賴關係。
該選項可以用來向標準輸出產生makefile依賴關係,可以把這些信息重定向到一個文件中以待進一步處理,比如:
nasm -M myfile.asm > myfile.dep
  • -F選項:選擇一個調試格式
該選項可以用來為輸出文件選擇一個調試格式,語法跟-f選項相同,僅有的不同是它產生的輸出文件是調試格式的。
一個具體文件格式的完整的可使用調試文件格式的列表可通過命令
nasm -f  -y
來得到。
這個選項在缺省狀態下沒有被構建時NASM。
  • -g選項:使調試信息有效。
該選項可用來在指定格式的輸出文件中產生調試信息。
  • -E選項:把錯誤信息輸入到文件。
在MS-DOS下,儘管有辦法,但要把程序的標準錯誤輸出重定向到一個文件還是非常困難的。因為NASM常把它的警告和錯誤信息輸出到標準錯誤設備,這將導致你在文本編輯器裏面很難捕捉到它們。
因此NASM提供了一個-E選項,帶有一個文件名參數,它可以把錯誤信息輸出到指定的文件而不是標準錯誤設備。所以你可以輸入下面這樣的命令來把錯誤重定向到文件:
nasm -E myfile.err -f obj myfile.asm
  • -s選項:把錯誤信息輸出到stdout
-s選項可以把錯誤信息重定向到stdout而不是stderr,它可以在MS-DOS下進行重定向。想要在彙編文件myfile.asm時把它的輸出用管道輸出給more程序,可以這樣:
nasm -s -f obj myfile.asm | more
  • -i選項:包含文件搜索路徑
當NASM在源文件中看到%include操作符時,它不僅僅會在當前目錄下搜索給出的文件,還會搜索-i選項在命令行中指定的所有路徑。所以你可以從宏定義庫中包含進一個文件,比如,輸入:
nasm -ic:\macrolib\ -f obj myfile.asm
(通常,在-i與路徑名之間的空格是允許的,並且可選的。)
NASM更多的關注源代碼級上的完全可移植性,所以並不理解正運行的操作系統對文件的命名習慣;你提供給-i作為參數的的字符串會被一字不差地加在包含文件的文件名前。所以,上例中最後面的一個反斜槓是必要的,在*nix下,一個尾部的正斜線也同樣是必要的。(當然,如果你確實需要,你也可以不正規地使用它,比如,選項-ifoo會導致'%incldue "bar.i'去搜索文件'foobar.i'...)
如果你希望定義一個標準的搜索路徑,比如像Unix系統下的/usr/include,你可以在環境變量NASMENV中放置一個或多個-i。為了與絕大多數C編譯器的Makefile保持兼容,該選項也可以被寫成-I。
  • -p選項: 預包含一個文件
NASM允許你通過-p選項來指定一個文件預包含進你的源文件。所以,如果運行:
nasm myfile.asm -p myinc.inc
跟在源文件開頭寫上'%include "myinc.inc"然後運行nasm myfile.asm是等效的。
為和-I、-D、-U選項操持一致性,該選項也可以被寫成-P
  • -d選項:預定義一個宏。
就像-p選項給出了在文件頭放置%include的另一種實現,-d選項給出了在文件中寫%define的另一種實現,你可以寫:
nasm myfile.asm -dFOO=100
作為在文件中寫下面一行語句的一種替代實現:
%define FOO 100
在文件的開始,你可以取消一個宏定義,同樣,選項-dFOO等同於代碼%define FOO。這種形式的操作符在選擇編譯時操作中非常有用,它們可以用%ifdef來進行測試,比如-dDEBUG。為了與絕大多數C編譯器的Makefile保持兼容,該選項也可以被寫成-D。
  • -u選項:取消一個宏定義。
-u選項可以用來取消一個由-p或-d選項先前在命令行上定義的一個宏定義。比如,下面的命令語句:
nasm myfile.asm -dFOO=100 -uFOO
會導致FOO不是一個在程序中預定義的宏。這在Makefile中不同位置重載一個操作時很有用。為了與絕大多數C編譯器的Makefile保持兼容,該選項也可以被寫成-U。
  • -e選項:僅預處理。
NASM允許預處理器獨立運行。使用-e選項(不需要參數)會導致NASM預處理輸入文件,展開所有的宏,去掉所有的註釋和預處理操作符,然後把結果文件打印在標準輸出上(如果-o選項也被指定的話,會被存入一個文件)。該選項不能被用在那些需要預處理器去計算與符號相關的表達式的程序中,所以如下面的代碼:
%assign tablesize ($-tablestart)
會在僅預處理模式中會出錯。
  • -a選項: 不需要預處理。
如果NASM被用作編譯器的後台,那麼假設編譯器已經作完了預處理,並禁止NASM的預處理功能顯然是可以節約時間,加快編譯速度。-a選項(不需要參數),會讓NASM把它強大的預處理器換成另一個什麼也不做的預處理器。
  • -On選項: 指定多遍優化。
NASM在缺省狀態下是一個兩遍的彙編器。這意味着如果你有一個複雜的源文件需要多於兩遍的彙編。你必須告訴它。使用-O選項,你可以告訴NASM執行多遍彙編。語法如下:
-O0
嚴格執行兩遍優化,JMP和Jcc的處理和NASM 0.98版類似,除了向後跳的JMP是短跳轉,如果可能,立即數在它們的短格式沒有被指定的情況下使用長格式。-O1嚴格執行兩遍優化,但前向分支被彙編成保證能夠到達的代碼;可能產生比-O0更大的代碼,但在分支中的偏移地址沒有指定的情況下彙編成功的機率更大,-On多編優化,最小化分支的偏移,最小化帶符號的立即數,當strict關鍵字沒有用的時候重載指定的大小,如果2<=n<=3,會有5*n遍,而不是n遍。注意這是一個大寫的O,和小寫的o是不同的,小寫的o是指定輸出文件的格式。
  • -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、endifif、ifdef、ifdifi、ifndef、include、local)
  • -w選項:使彙編警告信息有效或無效。
NASM可以在彙編過程中監視很多的情況,其中很多是值得反饋給用户的,但這些情況還不足以構成嚴重錯誤以使NASM停止產生輸出文件。這些情況被以類似錯誤的形式報告給用户,但在報告信息的前面加上“warning”字樣。警告信息不會阻止NASM產生輸出文件並向操作系統返回成功信息。
有些情況甚至還要寬鬆:他們僅僅是一些值得提供給用户的信息。所以,NASM支持-w命令行選項。它以使特定類型的彙編警告信息輸出有效或無效。這樣的警告類型是以命名來描述的,比如,orphan-labels,你可以以下列的命令行選項讓此類警告信息得以輸出:
-w+orphan-labels
或者以
-w-orphan-labels
讓此類信息不能輸出。
可禁止的警告信息類型有下列一些:
macro-params包括以錯誤的參數個數調用多行的宏定義的警告。這類警告信息缺省情況下是輸出的,至於為什麼你可能需要禁止它。
orphan-labels包含源文件行中沒有指令卻定義了一個沒有結尾分號的label的警告。缺省狀況下,NASM不輸出此類警告。
number-overflow包含那些數值常數不符合32位格式警告信息(比如,你很容易打了很多的F,錯誤產生了0x7fffffffffff)。這種警告信息缺省狀況下是打開的。
  • -v選項: 打印版本信息。
輸入
NASM -v
會顯示你正使用的NASM的版本號,還有它被編譯的時間。如果你要提交bug報告,你可能需要版本號。
如果你定義了一個叫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以後開始叫這個名字。

nasmMASM用户速成

如果你曾使用MASM寫程序,或者使用在MASM兼容模式下使用TASM, 或者使用a86,本節將闡述MASM與NASM語法之間的主要區別。如果你沒有使用過MASM,那最好先跳過這一節。
1.NASM是大小寫敏感的。
一個簡單的區別是NASM是大小寫敏感的。當你調用你的符號foo、Foo、或FOO時,它們是不同的。如果你在彙編DOS或OS/2、.OBJ文件,你可以使用UPPERCASE操作符來保證所有的導出到其他代碼模式的符號都是大寫的;但是,在僅僅一個單獨的模塊中,NASM會區分大小寫符事情。
2.NASM需要方括號來引用內存地址
NASM的設計思想是語法儘可能簡潔。它的一個設計目標是,它將在被使用的過程中,儘可能得讓用户看到一個單行的NASM代碼時,就可以説出它會產生什麼操作碼。你可以在NASM中這樣做,比如,如果你聲明瞭:
foo equ 1
bar dw 2
然後有兩行的代碼:
mov ax,foo
mov ax,bar
儘管它們有看上去完全相同的語法,但卻產生了完全不同的操作碼。NASM為了避免這種令人討厭的情況,擁有一個相當簡單的內存引用語未能。規則是任何對內存中內容的存取操作必須要在地址上加上方括號。但任何對地址值的操作不需要。所以,形如
mov ax,foo
的指令總是代表一個編譯時常數,不管它是一個 EQU或一個變量的地址;如果要取變量bar的內容,你必須與mov ax。這也意味着NASM不需要MASM的OFFSET關鍵字,因為MASM的代碼
mov ax,offset bar
同NASM的語法
mov ax,bar
是完全等效的。如果你希望讓大量的MASM代碼能夠被NASM彙編通過,你可以編寫%idefine offset讓預處理器把OFFSET處理成一個無操作符。這個問題在a86中就更混亂了。
NASM因為關注簡潔性,同樣不支持MASM和它的衍生產品支持的的混合語法,比如像:mov ax, table,這裏,一箇中括號外的部分加上括號內的一個部分引用一個內存地址,上面代碼的正確語法是:mov ax,[table+bx]。同樣,mov ax,es:[di]也是錯誤的,正確的應該是mov ax,[es:di]。
3.NASM不存儲變量的類型。
NASM被設計成不記住你聲明的變量的類型。然而,MASM在看到'var dw 0'時會記住類型,然後就可以隱式地合用mov var, 2給變量賦值。NASM不會記住關於變量var的任何東西,除了它的位置,所以你必須顯式地寫上代碼'mov word [var],2。因為這個原因,NASM不支持LODS、MOVS、STOS、SCANS、CMPS、INS或OUTS指令,僅僅支持形如LODSB、MOVSW和SCANSD之類的指令。它們都顯式的指定被處理的字符串的尺寸。
4.NASM不會ASSUME
作為NASM簡潔性的一部分,它同樣不支持'ASSUME'操作符。NASM不會記住你往段寄存器裏放了什麼值,也不會自動產生段替換前綴。
5.NASM不支持內存模型。
NASM同樣不含有任何操作符來支持不同的16位內存模型。程序員需要自己跟蹤那
些函數需要far call,哪些需要near call。並需要確定放置正確的'RET'指令(RETN或RETF;NASM接受RET作為RETN的另一種形式);另外程序員需要在調用外部函數時在需要的編寫CALL FAR指令,並必須跟蹤哪些外部變量定義是far,哪些是near。
6.浮點處理上的不同。
NASM使用跟MASM不同的浮點寄存器名:MASM叫它們ST(0)、ST(1)等,而a86叫它們0、1等,NASM則叫它們st0、st1等。NASM的0.96版以跟MASM兼容彙編器同樣的方式處理nowait形式的指令,0.95版以及更早的版本上的不同的處理方式主要是因為作者的誤解。
7.其他不同。
由於歷史的原因,NASM把MASM兼容彙編器的'TBYTE'寫成'TWORD'。NASM以跟MASM不同的一種方式聲明未初始化的內存。MASM的程序員必須使用stack db 64 dup (?),NASM需要這樣寫:
stack resb 64
讀作”保留64字節“。為了保持可移植性,NASM把”?“看作是符號名稱中的一個有效的字符,所以你可以編寫這樣的代碼
? equ 0
然後寫
dw ?
可以做一些有用的事情。DUP還是一個不被支持的語法。
另外,宏與操作符的工作方式也與MASM完全不同。

nasmNASM語言

nasm1、NASM源程序行的組成

就像很多其他的彙編器,每一行NASM源代碼包含(除非它是一個宏,一個預處理操作符,或一個彙編器操作符)label、instruction、operands、comment四個部分的全部或某幾個部分。通常,這些域的大部分是可選的;label、instruction、comment存在或不存在都是允許的。當然,operands域會因為instruction域的要求而必需存或必須不存在。
NASM使用\作為續行符;如果一個以一個\結束,那第二行會被認為是前面一行的一部分。
NASM對於一行中的空格符並沒有嚴格的限制:labels可以在它們的前面有空格,或其他任何東西。label後面的冒號同樣也是可選的。注意到,這意味着如果你想要寫一行lodsb,但卻錯誤地寫成了lodab,這仍將是有效的一行,但這一行不做任何事情,只是定義了一個label。運行NASM時帶上命令行選項
-w+orphan-labels
會讓NASM在你定義了一個不以冒號結尾的label時警告你。
labels中的有效的字符半角拉丁字母阿拉伯數字-$#@~.?。但只有符號.(具有特殊含義),_和?可以作為標識符的開頭。一個標識符還可以加上一個$前綴,以表明它被作為一個標識符而不是保留字來處理。這樣的話,如果你想到鏈接進來的其他模塊中定義了一個符號叫eax,你可以用$eax在NASM代碼中引用它,以和寄存器的符號區分開。
instruction域可以包含任何機器指令:Pentium指令、P6指令、FPU指令、MMX指令,甚至沒有公開的指令也會被支持。這些指令可以加上前綴LOCK、REP、REPE、REPZ、REPNE或REPNZ,通常,支持顯示的地址尺寸和操作數尺寸前綴A16、A32、O16和O32。關於使用它們的一個例子在第九章給出。你也可以使用段寄存器名作為指令前綴: 代碼
es mov ,ax
等效於代碼
mov [es:bx],ax
我們推薦後一種語法。因為它和語法中的其它語法特性一致。但是對於象LODSB這樣的指令,它沒有操作數,但還是可以有一個段前綴,對於es lodsb沒有清晰地語法處理方式在使用一個前綴時,指令不是必須的,像CS、A32、LOCK或REPE這樣的段前綴可以單獨出現在一行上,NASM僅僅產生一個前綴字節。作為對實際機器指令的擴展,NASM同時提供了一定數量的偽操作指令。
指令操作數可以使用一定的格式:它們可以是寄存器,僅僅以寄存器名來表示(比如:axbpebx、cr0)NASM不使用gas的語法風格,在這種風格中,寄存器名前必須加上一個%符號),或者它們可以是有效的地址,常數,或表達式
對於浮點指令,NASM接受各種語法:你可以使用MASM支持的雙操作數形式,或者你可以使用NASM的在大多數情況下全用的單操作數形式。支持的所以指令的語法
細節比如:
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
幾乎所有的浮點指令在引用內存時必須使用以下前綴中的一個DWORDQWORD或TWORD來指明它所引用的內存的尺寸。

nasm2、偽指令

偽指令是一些並不是真正的x86機器指令,但還是被用在了instruction域中的指令,因為使用它們可以帶來很大的方便。當前的偽指令有DBDWDDDQDT,它們對應的未初始化指令是RESB、RESW、RESD、RESQ、REST和INCBIN命令,EQU命令和TIMES前綴。
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不接受數值常數或字符串常數作為操作數
2. RESB類的偽指令:聲明未初始化的數據。
RESB、RESW、RESD、RESQ、REST被設計用在模塊的BSS段中:它們聲明未初始化的存儲空間。每一個帶有單個操作數,用來表明字節數,字數,或雙字數或其他的需要保留單位。NASM不支持MASM/TASM的扣留未初始化空間的語法DW ?或類似的東西:現在我們所描述的正是NASM自己的方式。RESB類偽指令操作數是有嚴格的語法的。
比如:
buffer: resb 64 ; reserve 64 bytes
wordvar: resw 1 ; reserve a word
realarray resq 10 ; array of ten reals
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
4. EQU:定義常數。
EQU定義一個符號,代表一個常量值:當使用EQU時,源文件行上必須包含一個label。EQU的行為就是把給出的label的名字定義成它的操作數(唯一)的值。定義是不可更改的,比如:
message db 'hello, world'
msglen equ $-message
把msglen定義成了常量12。msglen不能再被重定義。這也不是一個預自理定義:msglen的值只被計算一次,計算中使用到了$在此時的含義。注意EQU的操作數也是一個嚴格語法的表達式
5. TIMES:重複指令或數據。
前綴TIMES導致指令被彙編多次。它在某種程序上是NASM的與MASM兼容彙編器DUP語法的等價物。你可以這樣寫:
zerobuf: times 64 db 0
或類似的東西,但TIMES的能力遠不止於此。TIMES的參數不僅僅是一個數值常數,還有數值表達式,所以你可以這樣做:
buffer: db 'hello, world'times 64-$+buffer db ' '
它可以把buffer的長度精確地定義為64字節,TIMES可以被用在一般地指令上,所以你可像這要編寫不展開的循環:
times 100 movsb
注意在times 100 resb 1跟resb 100之間並沒有顯著的區別,除了後者在彙編時會快上一百倍。就像EQU、RESB它們一樣,TIMES的操作數也是嚴格語法的表達式。注意TIMES不可以被用在上:原因是TIMES在宏被分析後再被處理,它允許TIMES的參數包含像上面的64-$+buffer這樣的表達式。要重複多於一行的代碼,或者一個宏,使用預處理指令%rep。

nasm3、有效地址

一個有效地址是一個指令的操作數,它是對內存的一個引用。在NASM中,有效地址的語法是非常簡單的:它由一個可計算的表達式組成,放在一箇中括號內。比如:
wordvar dw 123
mov ax,[wordvar]
mov ax,[wordvar+1]
mov ax,[es:wordvar+bx]
任何與上例不一致的表達都不是NASM中有效的內存引用,比如es:wordvar。更復雜一些的有效地址,比如含有多個寄存器的,也是以同樣的方式工作:
mov eax,[ebx*2+ecx+offset]
mov ax,
NASM在這些有效地址上具有進行代數運算的能力,所以看似不合法的一些有效地址使用上都是沒有問題的:
mov eax,[ebx*5] ; assembles as [ebx*4+ebx]
mov eax,[label1*2-label2] ; ie [label1+(label1-label2)]
有些形式的有效地址在彙編後具有多種形式;在大多數情況下,NASM會自動產生最小化的形式。比如,32位的有效地址[eax*2+0]和[eax+eax]在彙編後具有完全不同的形式,NASM通常只會生成後者,因為前者會為0偏移多開闢4個字節。NASM具有一種隱含的機制,它會對[eax+ebx]和[ebx+eax]產生不同的操作碼;通常,這是很有用的,因為[esi+ebp]和[ebp+esi]具有不同的缺省段寄存器。儘管如此,你也可以使用關鍵字BYTE、WORD、DWORD和NOSPLIT強制NASM產生特定形式的有效地址
如果你想讓[eax+3]被彙編成具有一個double-word的偏移域,而不是由NASM缺省產生一個字節的偏移。你可以使用[dword eax+3],同樣,你可以強制NASM為一個第一遍彙編時沒有看見的小值產生一個一字節的偏移。比如:''。
有一種特殊情況,會被彙編成[eax+0]。帶有一個字節的0偏移。而[dwordeax]會帶一個double-word的0偏移。而常用的形式,[eax]則不會帶有偏移域。當你希望在16位的代碼中存取32位段中的數據時,上面所描述的形式是非常有用的。
實際上,如果你要存取一個在已知偏移地址處的數據,而這個地址又大於16位值,如果你不指定一個dword偏移,NASM會讓高位上的偏移值丟失。類似的,NASM會把[eax*2]分裂成[eax+eax],因為這樣可以讓偏移域不存在以此節省空間;實際上,它也把[eax*2+offset]分成[eax+eax+offset],你可以使用NOSPLIT關鍵字改變這種行為:
[nosplit eax*2]
會強制[eax*2+0]按字面意思被處理。

nasm4、常數

NASM能理解四種不同類型的常數:數值、字符字符串浮點數
1.數值常數。
一個數值常數就只是一個數值而已。NASM允許你以多種方式指定數值使用的進制,你可以以後綴H、Q、B來指定十六進制數,八進制數和二進制數,或者你可以用C風格的前綴0x表示十六進制數,或者以Borland Pascal風格的前綴$來表示十六進制數,注意,$前綴在標識符中具有雙重職責,所以一個以$作前綴的十六進制數值必須在$後緊跟數字,而不是字符。
請看一些例子:
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
2.字符型常數。
一個字符常數最多由包含在雙引號或單引號中的四個字符組成。引號的類型與使用跟NASM其它地方沒什麼區別,但有一點,單引號中允許有雙引號出現。一個具有多個字符的字符常數會被little-endian order,如果你編寫:
mov eax,abcd
產生的常數不會是0x61626364,而是0x64636261,所以你把常數存入內存的話,它會讀成abcd而不是dcba。這也是奔騰的CPUID指令理解的字符常數形式。
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的操作數時被處理成字符串。
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為了能夠處理浮點運算,它必須含有它自己的一套完整的浮點處理例程,它大大增加了彙編器的大小,卻獲得了並不多的好處。

nasm5、表達式

NASM中的表達式語法跟C裏的是非常相似的。NASM不能確定編譯時在計算表達式時的整型數尺寸:因為NASM可以在64位系統上非常好的編譯和運行,不要假設表達式總是在32位的寄存器中被計算的,所以要慎重地對待整型數溢出的情況。它並不總能正常的工作。NASM唯一能夠保證的是:你至少擁有32位長度。NASM在表達式中支持兩個特殊的記號,即$和$$它們允許引用當前指令的地址。$計算得到它本身所在源代碼行的開始處的地址;所以你可以簡單地寫這樣的代碼jmp $來表示無限循環。$$計算當前段開始處的地址,所以你可以通過($-$$)找出你當前在段內的偏移。NASM提供的運算符以運算優先級為序列舉如下:
1. |:位或運算符。
運算符|給出一個位級的或運算,所執行的操作與機器指令or是完全相同的。位或是NASM中優先級最低的運算符。
2. ^:位異或運算符。
^提供位異或操作。
3. &:位與運算符。
&提供位與運算。
4. << and >>:位移運算符。
<<提供位左移,跟C中的實現一樣,所以5<<3相當於把5乘上8。>>提供位右移。在NASM中,這樣的位移總是無符號的,所以位移後,左側總是以零填充,並不會有符號擴展。
5. + and -:加與減運算符。
+與-運算符提供完整的普通加減法功能。
6. *、/、//、%和%%:乘除法運算符。
*是乘法運算符。/和//都是除法運算符,/是無符號除,//是帶符號除。同樣的,%和%%提供無符號與帶符號的模運算。同ANSI C一樣,NASM不保證對帶符號模操作執行的操作的有效性。因為%符號也被宏預處理器使用,你必須保證不管是帶符號還是無符號的模操作符都必須跟有空格。
7. 一元運算符:+、-、~和SEG這些只作用於一個參數的一元運算符是NASM的表達式語法中優先級最的。-把它的操作數取反,+不作任何事情(它只是為了和-保持對稱),~對它的操作數取補碼,而SEG提供它的操作數的段地址。

nasm6、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沒有提供更便利的寫法,但你可以用宏自己建造一個。

nasm7、STRICT:約束優化

當在彙編時把優化器打開到2或更高級的時候。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有沒有使用,都會產生相同的代碼。

nasm8、臨界表達式

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足夠小,足以放在一個字節的偏移域中,所以,它以產生一個短形式的有效地址編碼的方式來解決這個問題;在第一遍中,它所知道的所有關於offset的情況是:它可能是代碼段中的一個符號,而且,它可能需要四字節的形式。所以,它強制這條指令的長度為適合四字節地址域的長度。在第二遍的時候,這個決定已經作出了,它保持使這條指令很長,所以,這種情況下產生的代碼沒有足夠的小,這個問題可以通過先定義offset的辦法得到解決,或者強制有效地址的尺寸大小,象這樣寫代碼:

nasm9、本地Labels

NASM對於那些以一個句點開始的符號會作特殊處理,一個以單個句點開始的Label會被處理成本地label, 這意味着它會跟前面一個非本地label相關聯。比如:
label1 ; some code
.loop
; some more code
jne .loop
ret
label2 ; some code
.loop
; some more code
jne .loop
ret
上面的代碼片斷中,每一個JNE指令跳至離它較近的前面的一行上,因為“.loop”的兩個定義通過與它們前面的非本地Label相關聯而被分離開來了。對於本地Label的處理方式是從老的Amiga彙編器DevPac中借鑑過來的;儘管如此,NASM提供了進一步的性能,允許從另一段代碼中調用本地labels。這是通過在本地label的前面加上非本地label前綴實現的:第一個“.loop”實際上被定義為label1.loop,而第二個符號被記作label2.loop。所以你確實需要的話你可寫:
label3 ; some more code; 
and some more
jmp label1.loop
有時,這是很有用的(比如在使用宏的時候),可以定義一個label,它可以在任何地方被引用,但它不會對常規的本地label機制產生干擾。這樣的label不能是非本地label,因為非本地label會對本地labels的重複定義與引用產生干擾;也不能是本地的,因為這樣定義的宏就不能知道label的全稱了。所以NASM引進了第三類label,它只在宏定義中有用:如果一個label以一個前綴..@開始,它不會對本地label產生干擾,所以,你可以寫:
label1: ; a non-local label
.local: ; this is really label1.local
..@foo: ; this is a special symbol
label2: ; another non-local label
.local: ; this is really label2.local
jmp ..@foo ; this will jump three lines up
NASM還能定義其他的特殊符號,比如以兩個句點開始的符號,比如..start被用來指定.obj輸出文件的執行入口。

nasm示例程序

這是一個DOS下的“Hello world!”程序:
section .text
org 0x100
	mov	ah, 0x9
	mov	dx, hello
	int	0x21

	mov	ax, 0x4c00
	int	0x21

section .data
hello:	db 'Hello, world!', 13, 10, '$'
一個類似程序在Microsoft Windows下的示例:
global _main
extern _MessageBoxA@16
extern _ExitProcess@4

section code use32 class=code
_main:
	push	dword 0      ; UINT uType = MB_OK
	push	dword title  ; LPCSTR lpCaption
	push	dword banner ; LPCSTR lpText
	push	dword 0      ; HWND hWnd = NULL
	call	_MessageBoxA@16

	push	dword 0      ; UINT uExitCode
	call	_ExitProcess@4

section data use32 class=data
	banner:	db 'Hello, world!', 0
	title:	db 'Hello', 0
一段Linux下的等價程序:
global _start

section .text
_start:
	mov	eax, 4 ; write
	mov	ebx, 1 ; stdout
	mov	ecx, msg
	mov	edx, msg.len
	int	0x80   ; write(stdout, msg, strlen(msg));

	mov	eax, 1 ; exit
	mov	ebx, 0
	int	0x80   ; exit(0)

section .data
msg:	db	"Hello, world!", 10
.len:	equ	$ - msg
下面是一個用於macOS的64位程序,用於輸入按鍵並將其顯示在屏幕上:
global _start

section .data

	query_string:		db	"Enter a character:  "
	query_string_len:	equ	$ - query_string
	out_string:			db	"You have input:  "
	out_string_len:		equ	$ - out_string

section .bss

	in_char:			resw 4

section .text

_start:

	mov	rax, 0x2000004	 	; put the write-system-call-code into register rax
	mov	rdi, 1				; tell kernel to use stdout
	mov	rsi, query_string	; rsi is where the kernel expects to find the address of the message
	mov	rdx, query_string_len	; and rdx is where the kernel expects to find the length of the message 
	syscall

	; read in the character
	mov	rax, 0x2000003		; read system call
	mov	rdi, 0				; stdin
	mov	rsi, in_char		; address for storage, declared in section .bss
	mov	rdx, 2				; get 2 bytes from the kernel's buffer (one for the carriage return)
	syscall

	; show user the output
	mov	rax, 0x2000004		; write system call
	mov	rdi, 1				; stdout
	mov	rsi, out_string
	mov	rdx, out_string_len
	syscall

	mov	rax, 0x2000004		; write system call
	mov	rdi, 1				; stdout
	mov	rsi, in_char
	mov	rdx, 2				; the second byte is to apply the carriage return expected in the string
	syscall

	; exit system call
	mov	rax, 0x2000001		; exit system call
        xor     rdi, rdi
	syscall

nasm鏈接

NASM主要輸出目標文件(擴展名一般為.obj),這些目標文件通常不能自行執行。唯一的例外是浮動二進制文件(例如.COM),它們在現代使用中固有地受到限制。 要將目標文件轉換為可執行程序,必須使用適當的鏈接程序,例如用於Windows的Visual Studio LINK實用程序或用於類Unix系統ld

nasm發展

第一版(版本號0.90)發佈於1996年10月。
2007年11月28日,NASM 2.00版發佈,增加對x86_64擴展的支持。 開發版本不再上傳到SourceForge.net;相反,它們會被檢入到項目自己的Git存儲庫中,而其二進制程序的快照可在項目官網上找到。
一個用於NASM文檔的搜索引擎也已可用。
截至2.07版本,NASM在簡化版BSD許可證(2-clause BSD License)下發布。

nasmRDOFF

RDOFF
全稱
Relocatable Dynamic Object File Format
漢字譯名
可重定位動態目標文件格式
開發者
Julian Hall
格式類型
Object file format
專門屬
Object code
開發人員使用可重定位的動態目標文件格式(RDOFF)來測試NASM的目標文件輸出能力的完整性。 它很大程度上基於NASM的內部結構,主要由一個頭部組成,頭部包含輸出驅動程序函數調用的序列化,後跟包含可執行代碼或數據的部分數組。 NASM發行版中包含了使用該格式的工具,包括鏈接程序和加載程序。
直到1996年10月發佈0.90版,NASM 才支持只輸出浮動格式的可執行文件(例如 DOS 的 COM 文件)。在版本0.90中,Simon Tatham 增加了對一個目標文件輸出接口的支持,並且只支持用於16位代碼的DOS的 .OBJ文件。
NASM因此缺少一個32位的對象格式。 為了解決這個問題,作為學習對象文件接口的練習,開發人員朱利安·霍爾將第一版RDOFF發佈於NASM的0.91版。
自從這個初始版本以來,對RDOFF格式進行了一次重大更新,它在每個標題記錄上增加了一個記錄長度指示器,[10] 允許程序跳過它們無法識別格式的記錄,並支持多個段;RDOFF1僅支持三段:文本、數據、bss(包含未初始化的數據)。
參考資料
  • 1.    Hans Anvin  .Computer Hope's Free Computer Help.2019-12-30[引用日期2021-03-02]
  • 2.    NASM  .NASM[引用日期2021-03-02]