?? o語言匯編基礎教程.htm
字號:
<TD WIDTH="34%" VALIGN="MIDDLE">
寄存器說明</TD>
</TR>
<TR><TD WIDTH="12%" VALIGN="TOP" ROWSPAN=8>
多媒體寄存器</TD>
<TD WIDTH="22%" VALIGN="TOP">
媒體零</TD>
<TD WIDTH="16%" VALIGN="MIDDLE">
MM0</TD>
<TD WIDTH="16%" VALIGN="TOP">
000</TD>
<TD WIDTH="34%" VALIGN="TOP">
媒體寄存器零</TD>
</TR>
<TR><TD WIDTH="22%" VALIGN="TOP">
媒體一</TD>
<TD WIDTH="16%" VALIGN="MIDDLE">
MM1</TD>
<TD WIDTH="16%" VALIGN="TOP">
001</TD>
<TD WIDTH="34%" VALIGN="TOP">
媒體寄存器一</TD>
</TR>
<TR><TD WIDTH="22%" VALIGN="TOP">
媒體二</TD>
<TD WIDTH="16%" VALIGN="MIDDLE">
MM2</TD>
<TD WIDTH="16%" VALIGN="TOP">
010</TD>
<TD WIDTH="34%" VALIGN="TOP">
媒體寄存器二</TD>
</TR>
<TR><TD WIDTH="22%" VALIGN="TOP">
媒體三</TD>
<TD WIDTH="16%" VALIGN="MIDDLE">
MM3</TD>
<TD WIDTH="16%" VALIGN="TOP">
011</TD>
<TD WIDTH="34%" VALIGN="TOP">
媒體寄存器三</TD>
</TR>
<TR><TD WIDTH="22%" VALIGN="TOP">
媒體四</TD>
<TD WIDTH="16%" VALIGN="MIDDLE">
MM4</TD>
<TD WIDTH="16%" VALIGN="TOP">
100</TD>
<TD WIDTH="34%" VALIGN="TOP">
媒體寄存器四</TD>
</TR>
<TR><TD WIDTH="22%" VALIGN="TOP">
媒體五</TD>
<TD WIDTH="16%" VALIGN="MIDDLE">
MM5</TD>
<TD WIDTH="16%" VALIGN="TOP">
101</TD>
<TD WIDTH="34%" VALIGN="TOP">
媒體寄存器五</TD>
</TR>
<TR><TD WIDTH="22%" VALIGN="TOP">
媒體六</TD>
<TD WIDTH="16%" VALIGN="MIDDLE">
MM6</TD>
<TD WIDTH="16%" VALIGN="TOP">
110</TD>
<TD WIDTH="34%" VALIGN="TOP">
媒體寄存器六</TD>
</TR>
<TR><TD WIDTH="22%" VALIGN="TOP">
媒體七</TD>
<TD WIDTH="16%" VALIGN="MIDDLE">
MM7</TD>
<TD WIDTH="16%" VALIGN="TOP">
111</TD>
<TD WIDTH="34%" VALIGN="TOP">
媒體寄存器七</TD>
</TR>
<TR><TD WIDTH="12%" VALIGN="MIDDLE" ROWSPAN=8>
單指令流、多數據流寄存器</TD>
<TD WIDTH="22%" VALIGN="TOP">
單流零</TD>
<TD WIDTH="16%" VALIGN="MIDDLE">
XMM0</TD>
<TD WIDTH="16%" VALIGN="TOP">
000</TD>
<TD WIDTH="34%" VALIGN="TOP">
單指令流、多數據流寄存器零</TD>
</TR>
<TR><TD WIDTH="22%" VALIGN="TOP">
單流一</TD>
<TD WIDTH="16%" VALIGN="TOP">
XMM1</TD>
<TD WIDTH="16%" VALIGN="TOP">
001</TD>
<TD WIDTH="34%" VALIGN="TOP">
單指令流、多數據流寄存器一</TD>
</TR>
<TR><TD WIDTH="22%" VALIGN="TOP">
單流二</TD>
<TD WIDTH="16%" VALIGN="TOP">
XMM2</TD>
<TD WIDTH="16%" VALIGN="TOP">
010</TD>
<TD WIDTH="34%" VALIGN="TOP">
單指令流、多數據流寄存器二</TD>
</TR>
<TR><TD WIDTH="22%" VALIGN="TOP">
單流三</TD>
<TD WIDTH="16%" VALIGN="TOP">
XMM3</TD>
<TD WIDTH="16%" VALIGN="TOP">
011</TD>
<TD WIDTH="34%" VALIGN="TOP">
單指令流、多數據流寄存器三</TD>
</TR>
<TR><TD WIDTH="22%" VALIGN="TOP">
單流四</TD>
<TD WIDTH="16%" VALIGN="TOP">
XMM4</TD>
<TD WIDTH="16%" VALIGN="TOP">
100</TD>
<TD WIDTH="34%" VALIGN="TOP">
單指令流、多數據流寄存器四</TD>
</TR>
<TR><TD WIDTH="22%" VALIGN="TOP">
單流五</TD>
<TD WIDTH="16%" VALIGN="TOP">
XMM5</TD>
<TD WIDTH="16%" VALIGN="TOP">
101</TD>
<TD WIDTH="34%" VALIGN="TOP">
單指令流、多數據流寄存器五</TD>
</TR>
<TR><TD WIDTH="22%" VALIGN="TOP">
單流六</TD>
<TD WIDTH="16%" VALIGN="TOP">
XMM6</TD>
<TD WIDTH="16%" VALIGN="TOP">
110</TD>
<TD WIDTH="34%" VALIGN="TOP">
單指令流、多數據流寄存器六</TD>
</TR>
<TR><TD WIDTH="22%" VALIGN="TOP">
單流七</TD>
<TD WIDTH="16%" VALIGN="TOP">
XMM7</TD>
<TD WIDTH="16%" VALIGN="TOP">
111</TD>
<TD WIDTH="34%" VALIGN="TOP">
單指令流、多數據流寄存器七</TD>
</TR>
</TABLE><br><br>
(表1.1 寄存器中文-英文命名對照表)<br>
注:英文名稱有星號“*”的表示作為保留域,實際并沒有使用,二進制碼有星號“*”表示無需二進制數表示<br><br>
匯編指令解析<br><br>
為什么研究匯編指令<br>
計算機思考的方式就是執行程序,這些程序都是由指令所構成的,計算機的指令是一串二進制碼數。精簡指令系統的指令長度等長,指令格式種類少,尋址方式種類少,大多數是簡單指令且都能在一個時鐘周期內完成,易于設計超標量與流水線,寄存器數量多,大量操作在寄存器之間進行,因此執行指令速度快。而復雜指令系統長度不等,指令格式繁多,尋址方式變化多樣,使指令的編寫與執行都錯綜復雜,這增加了處理器的硬件設計與處理指令的時間,復雜指令系統的形成是有它的歷史背景的,現在除了個人電腦市場還在用復雜指令集外,服務器及更大的系統早已不用了,它仍然存在的理由是為了兼容基于這種指令集平臺上的大量軟件,這也是我們研究復雜指令的理由,畢竟我們最終關心的還是豐富多彩的應用軟件。<br>
我們所說的編寫軟件就是要寫處理器能夠識別的二進制碼指令有序集合,讓處理器按照我們設計的方式來處理數據,最原始的計算機語言就是直接寫二進制碼,而現在已經有許多種語言可以方便地完成這樣的工作,人們熟知的語言有如C、PASCAL、BASIC、C++、VC、VB、DEPHI等等,它們最終無一例外地都要轉化成二進制指令,處理器才能夠識別并執行。現如今還有微軟推出的.NET框架下的一整套語言與人們熟知的JAVA語言是另一類型的計算機語言,它與上述語言不同的是,它的代碼并不被直接編譯成二進制可執行代碼,這是由它們的工作機制決定的,這一類型的語言被編譯后產生中間代碼并不直接與處理器打交道,而是通過虛擬機執行完成最終任務,因此它的代碼只要被編譯成虛擬機能夠識別就可以了,由于虛擬機處理代碼要額外消耗時間,因此執行效能相對較低,而且它們工作在虛擬機上,不能直接完成對底層的硬件操作。上述的語言人們把它們叫做高級語言,不需要用復雜的匯編指令來編寫程序,它們的語法容易為人們所理解和接受,學起來比較容易,但對中國人來說,這些語言都是以英文為媒介的,即便語法比較人性化,而英文本身對我們就是一道障礙,這也是為什么我們國家的軟件產業發展緩慢的重要原因,O語言的目標是徹底改變中國沒有計算機語言的歷史,最終使用符合我們習慣的語言。上述非工作在虛擬機上的高級語言被相應的編譯程序處理后,都被轉化成了匯編語言,最終由匯編程序來翻譯成二進制可執行代碼,直接與處理器打交道的匯編語言是一切計算機高級語言的基礎,因此,要改變我們沒有計算機語言的歷史,就必須從最底層的匯編語言做起。<br>
匯編指令的本源二進制機器指令<br>
由于匯編語言只是為了人們便于編寫二進制機器指令而寫的語言,它最終還是由機器指令來決定的,因此在學習匯編之前,首先了解二進制機器指令是有必要的。我們首先來看一串二進制數:1011 1000 0001 0010 0011 0100 這串二進制數用十六進制表示是0xB81234,大部分人看到這串數一定會一頭霧水,然而計算機處理器卻可以識別出這一串數字的意義,我們把上面的二進制數分成幾個部分來講解。從左到右取開始四位是:1011它是這一指令的主要部分,它實際上相當于指令的名稱,是用來區分不同指令的標識,它也決定了指令的功能。對應的英文匯編指令名稱是“MOV”,而O語言中是“傳送”指令。接下來的四位是:1000,它又分為1和000兩部分,第一位1決定后面三位的意義,后面的三位表示寄存器,如果第一位是1則指示后面的寄存器是字寄存器,即16位寄存器,如果是0則指示是字節寄存器,即8位寄存器,所以四位組合起來指定的就是一個16位寄存器,通過查表可得到000表示16位寄存器的是AX寄存器,也就是O語言里的累加一六寄存器。剩下的16位二進制碼做為最后一部分:0001 0010 0011 0100,它就是數字0x1234。顯然這條指令就是一條數據傳送指令,它的意義是把0x1234這個數傳送到累加一六這個寄存器中,不管原先累加一六寄存器中是什么數,執行這條指令后,它都會變成是0x1234。我們把這一條指令的前四位的指令標識叫做<U>操作碼</U>,而后面的寄存器包括數0x1234都叫做<U>操作數</U>,縱觀指令集,所有的指令都是由操作碼及操作數組成的,只不過有些指令不需要操作數,而只有操作碼。有一點需要說明的是,上面的例子在16位指令模式下是成立的,如果是32位指令模式則稍有不同,指示寄存器的位如果是1則指示的是雙字寄存器,即32位寄存器,如果是0則依然指示為字節寄存器,當指示為32位寄存器時后面的操作數也必須是32位即0x00001234,這時的指令二進制碼就應該為:1011 1000 0000 0000 0000 0000 0001 0010 0011 0100,它的意義是:把0x00001234這個數傳送到累加三二寄存器中。16位模式指令適用于286之前的處理器,32位指令模式適用于386之后的處理器,386之后的處理器既可以工作在16位模式下,也可以工作在32位模式下,通常我們把工作在16位模式下稱為實模式,而工作在32位模式稱為保護模式,它們的尋址方式有很大不同,如果要想在386以上的處理器中處于32位模式下使用16位的指令則必須在指令前加上0x66前綴。關于<U>尋址方式</U>和<U>指令前綴</U>后面章節會詳細闡述。<br>
一、操作碼 由前面例子我們大致知道了什么是操作碼,所謂操作碼就是唯一代表著指令的意義的一段二進制碼,操作碼通常占一個字節或者是兩個字節,如果你看到不同指令的前一個或前兩個字節完全相同也不用奇怪,它們是操作碼,但它們的意義還決定于后面的操作數字節,特別是第一個寄存器位。還有一種特殊的操作碼,它與寄存器操作數合起來共用一個字節或者兩個字節,且寄存器的二進制代碼都為這操作碼字節的最后三位,上節中所舉的數據傳送的例子就屬于這種特例的操作碼。我們使用匯編程序來編寫程序時指令助記符通常表示的就是操作碼,但實事上二進制操作碼與匯編指令助記符并非一一對應,一條匯編指令的名稱如:傳送指令,它可以對應多個操作碼,只有當指令名稱與指令操作數合在一起,才與操作碼和操作數有一一對應的關系。也就是說同一個指令名稱,指令操作數類型不同,對應的二進制操作碼也可能不同。<br>
在操作碼字節中并非如上介紹的那么簡單,只標識著指令的功能,它與操作數是息息相關的,在操作碼字節中通常有方向位、符號位、操作數大小修飾位,這些位都用一個二進制位來表示,方向位與符號位都在操作碼的倒數第二位,在同一時刻只表示一種意義,要么表示操作數的方向,要么表示操作數的符號擴展,而操作數修飾位通常在操作碼的最后一位。同樣我們也舉例說明,請看下面的這串二進制數:1000 1001 1001 1000 0000 0000 0000 0000 0001 0010 0011 0100,它用十六進制表示是0x899800001234,假設當前處理器運行在32位模式下,如果你能夠直接看懂這一長串的二進制數,那么祝賀你已經擁有世界上運行最快的人腦,無需再閱讀后面的教程了。如果不行請看我下面的分析。開始的字節1000 1001表示的是操作碼,這個字節的倒數第二位就用來表示操作數的流向,為0時表示從左流向右=>,為1時表示從右流向左<=,字節的最后一位指示操作數的大小,為0時表示字節操作數,即8位操作數,為1時表示雙字操作數,即32位操作數。它的操作碼同樣對應的也是傳送指令,英文匯編為MOV指令,這就映證了一個匯編助記符對應多個操作碼的情況。接下來的字節是1001 1000,它又可以分為三個部分,開始的兩位10代表的是模數(MOD),后三位011是寄存器操作數,再后三位000在這里代表的內存操作數,本指令剩下的32位數即0x00001234則代表的是內存操作數的位移量。這些意義是如何區分的呢?主要是由開始的兩位模數來決定,如果模數不是10而是11,那么它指示的是兩個操作數都是32位寄存器,如果模數為00則它指示的是模數之后的三位代表32位寄存器,而接下來的三位代表的是內存寄存器,且沒有位移量,如果模數為01則它指示的意義與00相似,但有8位的位移量。通過查寄存器對照表可知,011代表32位寄存器是EBX也就是O匯編語言里描述的基址三二寄存器,而000代表的32位寄存器是EAX即累加三二寄存器。因此,這條冗長的指令實際代表的意義就是,將基址三二寄存器內的數據傳送到指定的內存位置,這個內存地址由累加三二寄存器的值再加上0x00001234位移量得到。下表可以直觀地得到這條指令的意義:<br>
<TABLE BORDER CELLSPACING=1 CELLPADDING=7 WIDTH=539>
<TR><TD WIDTH="39%" VALIGN="TOP" COLSPAN=3 HEIGHT=28>
操作碼(8位)</TD>
<TD WIDTH="61%" VALIGN="TOP" COLSPAN=4 HEIGHT=28>
操作數(40位)</TD>
</TR>
<TR><TD WIDTH="14%" VALIGN="MIDDLE">
指令<br>
標識碼</TD>
<TD WIDTH="11%" VALIGN="MIDDLE">
操作數<br>
流向</TD>
<TD WIDTH="13%" VALIGN="MIDDLE">
操作數<br>
大小</TD>
<TD WIDTH="20%" VALIGN="MIDDLE">
模數</TD>
<TD WIDTH="13%" VALIGN="MIDDLE">
寄存器</TD>
<TD WIDTH="13%" VALIGN="MIDDLE">
內存<br>
寄存器</TD>
<TD WIDTH="16%" VALIGN="MIDDLE">
位移量</TD>
</TR>
<TR><TD WIDTH="14%" VALIGN="MIDDLE">
1000 10</TD>
<TD WIDTH="11%" VALIGN="MIDDLE">
0</TD>
<TD WIDTH="13%" VALIGN="MIDDLE">
1</TD>
<TD WIDTH="20%" VALIGN="MIDDLE">
10</TD>
<TD WIDTH="13%" VALIGN="MIDDLE">
011</TD>
<TD WIDTH="13%" VALIGN="MIDDLE">
000</TD>
<TD WIDTH="16%" VALIGN="MIDDLE">
0x00001234</TD>
</TR>
<TR><TD WIDTH="14%" VALIGN="MIDDLE">
匯編助記符為:傳送</TD>
<TD WIDTH="11%" VALIGN="MIDDLE">
從左<br>
到右</TD>
<TD WIDTH="13%" VALIGN="MIDDLE">
32位<br>
操作數</TD>
<TD WIDTH="20%" VALIGN="MIDDLE">
內存操作數有32位位移量</TD>
<TD WIDTH="13%" VALIGN="MIDDLE">
基址三二</TD>
<TD WIDTH="13%" VALIGN="MIDDLE">
累加三二</TD>
<TD WIDTH="16%" VALIGN="MIDDLE">
32位<br>
位移量</TD>
</TR>
</TABLE><br>
(表2.1 二進制指令分析)<br>
如果指令工作在16位模式下則稍有不同,位移量為16位即0x1234且操作數為16位,指令的意義表示為:將基址一六內的數據傳送到累加一六內的值加上0x1234指定的內存所在的位置。在使用匯編語言編寫程序時可以指定編寫的是16位模式指令還是32位模式指令,不同的位模式指令實用于不同的操作系統,如今常用到的都是32位的模式指令,只有在特殊的情況下才有必要編譯成16位模式指令。<br>
二、操作數 前面講述操作碼時有講到操作數,但僅做了簡單的介紹。計算機指令按其功能可簡單分為:常用指令、浮點指令、多媒體指令和單指令流多數據流(SSE)指令。匯編指令因其繁多且對應的二進制代碼有多種變化而變得異常復雜,而且操作數又存在多種情形,使大多數人談到匯編就望而生畏。實際上它們是有規律可循的,就指令與操作數的關系而言,指令無非是無操作數、單操作數、雙操作數和三操作數這幾種情況。最為簡單的指令就是無操作數指令,這種指令只含有操作碼,比如暫停指令,英文匯編指令助記符是HLT,它只有操作碼0xF4,比這稍微復雜點的情況就是有一個操作數的情形。操作數既可以是寄存器、內存地址也可以是立即數。前面的第一個例子是操作數為寄存器和立即數的情況,而第二個例子是寄存器和內存操作數的情況。處理器是如何來區分這些不同的操作數的呢?這主要是由不同的操作碼來決定,因此,我們說同一個匯編指令名稱助記符會對應多個操作碼原因就在于此,它們的功能是相似的,使用相同的助記符便于記憶和使用,匯編編譯程序在翻譯匯編指令時,可以根據相同的指令名稱與不同的操作數組合,翻譯成與之匹配的操作碼和操作數,使指令二進制碼與匯編語句一一對應,而反匯編程序恰恰相反,根據正確的指令二進制碼唯一地翻譯成一條對應的匯編語句。通常情況下,當指令操作數含有立即數時與不含立即數時的操作碼不同;指令操作數都為寄存器時,通用寄存器、段寄存器、控制寄存器、調試寄存器和任務寄存器的指令操作碼不同;多媒體指令、浮點指令和單指令流多數據流指令(SSE)都有各自的指令系統。當操作數為寄存器或內存時變化最為復雜,主要是因為在這種情況下,由一個叫做模數(MOD)的兩位二進制數來決定寄存器、內存以及內存位移量的多種情況。<br>
當操作數是寄存器或是內存地址時,主要是用一個字節來區分,這個字節的前兩位就是模數(MOD),后三位是寄存器的二進制代碼,最后三位的值由開始兩位的模數來決定具體的類型。如果模數為11那么最后三位就用來表示寄存器。內存地址是用寄存器的值來表示的,有的內存地址帶有位移量,于是,當模數為00時字節的最后三位就表示寄存器內的數據是內存地址,并且沒有位移量,當模數為01時,字節最后三位表示寄存器內的數據是內存地址,并且在這一字節之后有8位數的位移量,當模數為10時字節后面有16位或32位,是16位還是32位由匯編模式來決定。16位模式常用于實模式,可使用16位寄存器,如AX、BX等,32位常用于保護模式,可使用32位寄存器,如EAX、EBX等。然而,只用一個字節的最后三位來表示內存地址,不可能用來表示眾多的寄存器組合,于是模數為00時(沒有位移量),就有在16位匯編模式,當nnn=000(nnn表示是用來表示內存的三位二進制數)時,內存地址為DS:[BX+SI],在32位匯編模式時nnn=000,內存地址為DS:[EAX],其它的值請參照后面的對應表。于是,我們知道了,計算機指令是通過模數、寄存器和寄/內三個域來決定操作數的,其實除此之外,還有一種特殊的情況,就是操作數并沒有都用到這三個域,而是把第二個域的值設為一個預設值,也就是說,第二個用來表示寄存器的域設成了固定值,而不是可變的寄存器,那么這就出現了只有一個寄存器或是內存地址操作數的變型,其實它是由前一種類型變化得來的。<br>
<TABLE BORDER CELLSPACING=1 CELLPADDING=7 WIDTH=540>
<TR><TD WIDTH="15%" VALIGN="TOP" HEIGHT=50>
模數<br>
MOD</TD>
<TD WIDTH="20%" VALIGN="TOP" HEIGHT=50>
寄存器<br>
jjj</TD>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -