20192425蘇祺彙編程式設計前四章學習筆記
第一次作業 緩衝區溢位之彙編程式設計基礎
1.1.1前言
1.1.2組合語言的一般概念
組合語言(Assembly Language)是任何一種用於電子計算機、微處理器、微控制器或其他可程式設計器件的低階語言,亦稱為符號語言。在組合語言中,用助記符代替機器指令的操作碼,用地址符號或標號代替指令或運算元的地址。在不同的裝置中,組合語言對應著不同的機器語言指令集,通過彙編過程轉換成機器指令。特定的組合語言和特定的機器語言指令集是一一對應的,不同平臺之間不可直接移植。
1.2學習和使用匯編語言的目的
-
嵌入式驅動開發
-
反彙編源程式理解其本質
-
在應對惡意程式碼時,需要反編譯,看彙編程式碼
-
可以輕鬆的讀取儲存器狀態以及硬體I/O介面情況
-
編寫的程式碼因為少了很多編譯的環節,可以能夠準確的被執行
-
作為一種低階語言,可擴充套件性很高
組合語言的主體是彙編指令。
彙編指令和機器指令的差別在於指令的表示方法上。彙編指令是機器指令便於記憶的書寫格式。
彙編指令是機器指令的助記符。
IBM-PC微機的功能結構
IBM-PC微機基本結構
微機的一般構成
-
微機的一般構成:運算器、控制器、儲存器、輸入裝置和輸出裝置
-
其中將運算器和控制器兩大部件整合在一個積體電路晶片上,稱為中央處理器,簡稱CPU
-
系統採用匯流排結構,系統匯流排分為地址匯流排、資料匯流排、控制匯流排三類
-
主儲存器:用來存放程式和資料的部件,由若干儲存單元組成,對每個儲存單元按照地址進行訪問
-
指示儲存單元編號的地址長度決定了儲存器的最大容量,例如一個10位二進位制數表示的地址可以用來區分210=1K個單元
-
除主存外,計算機一般還配置輔助儲存器,也成為外存
-
習慣上將CPU與主儲存器合稱為主機
-
輸入輸出裝置及介面
-
I/O裝置的工作速度、工作原理以及所處理的資訊格式等與主機相差很大,因此I/O裝置要通過I/O介面與系統匯流排連線
Intel 8086/8088 CPU的功能結構
組合語言程式由一系列的指令(指令序列)構成。
CPU執行指令序列就是重複“取指令——執行指令”兩個步驟
-
序列方式
特點:系統匯流排空閒時間較多
-
指令流水線方式
執行單元(EU)與匯流排介面單元(BIU)
特點:減少了系統匯流排的空閒時間
通用暫存器
資料暫存器
包括AX、BX、CX、DX四個暫存器,它們既可以是16位暫存器,也可以當作8個獨立的8位暫存器使用。存放運算元或運算的結果。
例如:MOV AX,BX; 將BX的內容送到AX中。
ADD CH,DH; 將DH和CH的內容相加,結果放入CH。
在某些指令中,不需要明確指出使用的暫存器名,隱含使用對應暫存器。例如在迴圈指令LOOP中,CX被隱含指定作迴圈次數計數用;移位指令SHL AX,CL;CL被固定用作移位次數。
指標暫存器
有堆疊指標SP和基址指標BP,一般被用來存放16位地址,在形成20位的實體地址時常被用做偏移量使用。
SP指標——在進行堆疊操作時被隱含使用,用來指向堆疊頂部單元。
BP指標——被用來指向堆疊段內某一儲存單元。
變址暫存器
有兩個16位的變址暫存器SI(源變址暫存器)和DI(目的變址暫存器),一般被用來作地址指標,也可以作為通用資料暫存器存放運算元和運算結果
段暫存器
程式碼段,資料段,堆疊段,附加段
地址增長方向:從上往下
CPU在訪問儲存器時必須指明:
·所訪問的儲存單元屬於哪個段
·該儲存單元與段基址的偏移量
指令指標IP
指令指標暫存器IP(X86型CPU)相當於ARM型CPU中的程式計數器PC,用於控制程式中指令的執行順序。
正常執行時,IP中含有BIU要取的下一條指令(位元組)的偏移地址,一般情況下,每從記憶體中存取一次指令碼,IP就自動加1,從而保證指令的順序執行。
IP實際上是指令機器碼存放記憶體單元的地址指標,IP的內容可以被轉移類指令(如JMP)強迫改寫,以改變程式執行的順序。
注意!我們編制的程式不能直接訪問IP,即不能用指令去取出IP的值或給IP設定值(如不能用mov指令給IP賦值)。
標誌暫存器
用來反映CPU在程式執行時的某些狀態(是否進位、奇偶性、結果的符號、結果是否為零等)
8086/8088CPU標誌暫存器長度為16位,但只定義了其中的9位
第三章定址方式與指令系統
定址方式:尋找指令中所需運算元的各種方法,也就是提供指令中運算元的存放資訊的方式。
Intel 8086/8088各指令中提供運算元的方法有一下四種:
- 立即數運算元——運算元在指令程式碼中提供。立即數可以是8位,也可以是16位
- 暫存器運算元——運算元在CPU的通用暫存器或段暫存器中
- 儲存器運算元——運算元在記憶體的儲存單元中
- I/O埠運算元—— 運算元在輸入/輸出介面的暫存器中
儲存器儲存單元的邏輯地址由端基值和偏移量組成。
-
位移量:指令中直接給出的一個8位或16位數,一般源程式中以運算元名字(變數名或標號)的形式出現。
-
基址:由基址暫存器BX或基址指標BP提供的內容。
-
變址:由源變址暫存器SI或目的變址暫存器DI提供的內容。
立即數定址
立即數定址方式的指令中,所需運算元直接包含在指令 程式碼中,這種運算元稱為立即數。
暫存器定址
暫存器定址方式是指指令中所需的運算元在CPU的某個 暫存器中。暫存器可以是8位或16位通用暫存器,或者是段暫存器。
直接定址
指令由操作碼+運算元組成,運算元的有效地址EA只有位移量地址分量。
(1)用符號表示
例:MOV BX, VAR =>MOV BX, DS: VAR 它表示將資料段中,偏移了VAR個位元組距離的字單元 內容送到暫存器BX中。
(2)用常數表示
例:MOV AX, DS: [64H]
它表示從當前資料段開始,偏移100個位元組的字單元內容 送到AX中。
注意:用常數表示時,必須用方括號括起來。段暫存器不能省略。
暫存器間接定址
運算元有效地址EA直接從基址暫存器(BX或BP)或 變址暫存器(SI或DI)中獲得。
基址定址和變址定址
運算元的有效地址EA等於基地址分量或變址分量加上 指令中給出的位移量。
指令中使用BX或BP時為基址定址。指令中使用SI或DI時 為變址定址。
基址變址定址
運算元的有效地址是三個地址分量之和,即:EA=基址+變址+位移量
當基址選用BX時隱含使用段暫存器DS,而選用BP時 則隱含使用段暫存器SS。
串操作定址方式
在尋找源運算元時,隱含使用SI作為地址指標。
在尋找目的串時,隱含使用DI作為地址指標。
在串操作完成之後,自動對SI和DI進行修改,使它們指 向下一個運算元。
I/O埠定址
-
儲存器編址方法
將I/O埠視為儲存器的一個單元,對埠的訪問就如 同訪問儲存單元一樣。訪問儲存器的指令和各種定址方式 同樣適用對I/O埠的訪問。特點:程式設計靈活,但需要佔用儲存地址空間。
-
I/O埠編址方法
I/O埠的地址與儲存器地址分開,並使用專門的輸入指令和 輸出指令。直接埠定址
在指令中直接給岀埠地址,埠地址一般採用2位十六進 制數,也可以用符號表示。暫存器間接埠定址
暫存器間接埠定址:把I/O埠的地址先送到DX中,用 DX作間接定址暫存器。
如果訪問的埠地址值大於255,則必須用I/O埠的間 接定址方式。
指令系統
一種計算機所能執行的各種型別的指令的集合稱為該計算機的指令系統。
Intel8086/8088CPU指令系統的指令可以分為六大類:
1.傳送類指令
2.算術運算類指令
3.位操作類指令
4.串操作類指令
5.程式轉移類指令
6.處理器控制類指令
從指令的格式劃分,一般可以分為三種:
1.雙運算元指令:OPR DEST SRC
2.單運算元指令:OPR DEST
3.無運算元指令:OPR
對於無運算元指令,包含兩種情況: (1)指令不需要運算元,如暫停指令HLT。(2)在指令格式中,沒有顯式地指明運算元,但是它隱含指明瞭運算元的存放地方,如指令PUSHF。
傳送類指令
傳送類指令的作用是將資料資訊或地址資訊傳送到一個暫存器或儲存單元中,可以分為以下四種情況。
-
通用資料傳送指令
指令格式:MOV DEST,SRC
作用:將源運算元指定的內容傳送到目的運算元,即DEST<=(SRC)。
當指令執行完後,目的運算元原有的內容被源運算元內容覆蓋,即目的運算元和源運算元具有相同內容。
MOV指令對標誌暫存器的各位無影響。
MOV指令可以是位元組資料傳送也可以是字資料傳送,但是源運算元和目的運算元的長度必須一致。
MOV指令可以分為以下幾種情況:
(1)立即數傳送到通用暫存器或儲存單元 例:MOV AH,10H 注意:立即數只能作為源運算元,立即數不能傳送給段暫存器。 (2)暫存器之間的傳送 例:MOV AH,CH MOV AX,CS;正確 MOV CS,AX;錯誤 注意:段暫存器CS只能作源運算元,不能作目的運算元。 (3)暫存器與儲存單元之間傳送
綜合起來,MOV指令在使用時需注意以下幾個問題:
(1)立即數只能作源運算元,且它不能傳送給段暫存器。 (2)段暫存器CS只能作源運算元,段暫存器之間不能直接傳送。 (3)儲存單元之間不能直接傳送資料 (4)MOV指令不影響標誌位
-
交換指令
作用:源運算元和目的運算元兩者內容相互交換,即:(DEST)<=>(SRC)。
指令對標誌暫存器各位無影響。
資料交換可以在暫存器之間或暫存器與儲存器單元之間進行。但是不能在儲存單元之間直接進行資料交換。暫存器只能使用通用暫存器。
-
標誌傳送指令
(1)取標誌暫存器指令
指令格式: LAHIF
作用:將標誌暫存器的低8位送入AH暫存器,即將標誌SF、ZF、AF、PF和CF分別送入AH的第7、6、4、2、0位,而AH的第5、3、1位不確定。
(2)儲存標誌暫存器指令
指令格式:SAHF
作用:將暫存器AH中的第7、6、4、2、0位分別送入標誌暫存器的SF、ZF、AF、PF和CF各標誌位。而標誌暫存器高8位中的各標誌位不受影響。
(3)標誌進棧指令
指令格式:PUSHF
作用:先將堆疊指標SP減2,使其指向堆疊頂部的空字單元,然後將16位標誌暫存器的內容送SP指向的字單元。
(4)標誌出棧指令
指令格式:POPF
作用:將由SP指向的堆疊頂部的一個字單元的內容送入標誌暫存器,然後SP的內容加2.
-
地址傳送指令
將儲存單元的地址送暫存器。
(1)裝入有效地址
格式:LEA,DEST,SRC
源運算元必須是一個地址,目的是一個16位通用暫存器。作用是將SRC儲存單元地址中的偏移量,即有效地址EA傳送到一個16位通用暫存器中。指令執行對標誌暫存器各位無影響。
(2)裝入地址指標指令
格式:LDS DEST,SRC/LES DEST,SRC
-
算術運算類指令
1)加法指令:指令格式:ADD
DEST,SRC。DEST只能是通用暫存器或儲存器運算元,不能是立即數。
DEST和SRC不能都為儲存器運算元。可以是位元組運算元相加,也可以是字運算元相加。2)帶進位加法指令:指令格式:ADC DEST,SRC。與ADD不同的是結果要加上進位標誌CF的值。注意CF是本條指令執行之前的值。
3)加1指令:指令格式:INC
DEST,目的運算元可以是任意的8位16位通用暫存器或儲存器運算元,它不影響CF,INC指令主要用於某些計數器的計數和修改地址指標。4)減法指令:指令格式:SUB
DEST,SRC,,目的-源,再放入目的地址。不能同時為儲存器運算元減法指令對借位標誌的影響,若採用變減為加的運算方法,則產生的進位與CF標誌結果相反。5)帶借位減法:指令格式:SBB DEST,SRC,其實就是多減了一個CF和帶進位加法類似。
6)減1指令:指令格式:DEC DEST
7)求負數指令:指令格式:NEG
DEST,也叫做取補指令。對進位CF的影響:只有當運算元為0的時候,CF才被置0。當位元組運算元為-128,或字運算元為-32768時,執行NEG指令的結果運算元將無變化,但OF被置1.
加減乘除指令,這些指令可以對位元組資料或字資料進行運算,參加運算的可以是無符號數,也可以是帶符號數,帶符號數用補碼錶示。 -
位操作類指令
(1)邏輯運算指令,邏輯運算指令共有4條,它們的指令格式分別是邏輯“與”指令:AND
DEST,SRC邏輯“或”指令:OR DEST,SRC
邏輯“異或”指令:XOR DEST,SRC
邏輯“非”指令:NOT DEST
這四條指令都是執行按位邏輯運算。
邏輯指令對標誌位的影響:NOT對標誌無影響,而其餘三條指令將根據結果影響SF、ZF和PF,而CF和OF總是置0,AF為不確定。可以對一些數分離其高位或低位,或者把最低位置1,最高位置0,取反等等。
(2)測試指令:TEST
DEST,SRC。與AND類似,但是運算的結果不送入目的運算元。主要用於測試某一運算元的一位或幾位的狀態。(3)移位/迴圈移位指令
-
處理器控制類指令
(1)標誌位操作指令,能直接操作的標誌位有CF,IF和DF。
1.清除進位標誌:CLC:置CF為0.
2.置1進位標誌:STC:置CF為1
3.進位標誌取反:CMC:CF的值取反
4.清除方向標誌:CLD:置DF為0
5.置1方向標誌:STD:置DF為1
6.清除中斷標誌:CLI:置IF為0
7.置1中斷標誌:CTI
(2)與外部事件同步的指令:ESC,HLT,WAIT,LOCK
(3)空操作指令:NOP,相當於延時,三個CPU的時鐘週期。
指令編碼
雙運算元指令編碼格式
操作特徵部分
這部分為指令編碼的首位元組,它又分為以下三個段。
(1) OPCODE:操作碼欄位
該欄位長度為6bit。它表示了該指令所執行的功能 和兩個運算元的來源。
(2)方向欄位d
該欄位與第2部分定址特徵一起來決定源運算元和 目的運算元的來源。
(3)字/位元組欄位W
當W=1時,表示兩運算元長度為字;當W=0時,表 示兩運算元長度為位元組。
定址特徵部分
當d=l時,則目的運算元由REG欄位確定,而源操作 數由MOD和R/M欄位確定。
當d=0時,則目的運算元由MOD和R/M欄位確定,而 源運算元由REG欄位確定。
位移量部分
根據定址特徵中MOD和R/M欄位確定的有效地址 計算方法,位移量可以是以下三種情況之一:
- 沒有位移量
- 1位元組位移量disp8
- 2位元組位移量displ6
立即數部分
如果指令的源運算元為立即數,則指令編碼中包含 有該部分。它總是位於指令編碼的最後1〜2位元組。
單運算元指令編碼格式
這種編碼格式適用於只有一個運算元的指令,如INC、 DEC、移位/迴圈等指令。指令編碼為2〜3位元組。
操作特徵部分:
包括OPCODE、V和W三個欄位,其中V欄位只有移位/ 迴圈指令中才有該欄位。其它指令中沒有該欄位。
- V=0時,指令中使用常數1作為移位或迴圈次數。
- V=1時,指令中使用暫存器CL作移位次數。
由於單運算元指令中只有一個運算元,因此定址特徵部分就不需要REG欄位,而該欄位被用作輔助操作碼。
與AX或AL有關的指令編碼格式
這種編碼格式用於隱含指定AX/AL作為一個運算元 的雙運算元指令。
採用這種編碼格式的指令,除一個運算元隱含指定為 AX/AL外,另一個運算元可以是立即數或儲存單元。
立即數:則編碼中應有1〜2位元組的立即數。
儲存單元:只能使用直接定址方式,位 移量由disp欄位給岀。
其它指令編碼格式
除上述三種編碼格式外,還有一些指令的編碼格式更簡單。如標誌位操作指令、堆疊操作指令等。 這些指令的編碼格式一般只有一個位元組。
第四章 組合語言程式格式
通用資料傳送指令
組合語言由以下3類組成
- 彙編指令(機器碼的助記符)
- 偽指令(由編譯器執行)
- 其他符號(由編譯器識別)
內部匯流排實現CPU內部各個期間之間的聯絡
外部匯流排實現CPU和主機板上其他器件的聯絡
組合語言程式格式
組合語言的語句可以分為指令語句和偽指令語句
一條指令可以有一個運算元、兩個運算元或者無運算元。
如ADD、MOV指令需要兩個運算元,INC、NOT指令只需一個運算元,而CLC指令不需要運算元。
偽指令是由編譯器來執行的指令,編譯器根據偽指令來進行相關的編譯工作。
segment和ends是一對成對使用的偽指令。
end是一個彙編程式的結束標記。
程式返回:
```
mov ax,4c00H
int 21H
```
組合語言資料
資料是指令和偽指令語句中運算元的基本組成部分。一個數據由數值和屬性兩部分構成。
在組合語言中常用的資料形式有:常數、變數和標號。
常數在程式中可以用在以下幾種情況
-
作指令語句的源運算元
-
在指令語句的直接定址方式、變址(基址)定址方式或基址變址定址方式中作位移量
-
在資料定義偽指令中使用
變數用來表示存放資料的儲存單元,在程式執行期間可以被改變。
可以認為變數名就是存放資料的儲存單元地址
變數的使用
(1)在指令語句中引用
當變量出現在變址(基址)定址或基址變址定址的運算元中時表示取用該變數的偏移量。(2)在偽指令語句中引用
它表示取變數地址的偏移量
標號寫在一條指令的前面,它就是該指令在記憶體的存放地址的符號表示,也就是指令地址的別名。
標號主要用在程式中需要改變程式的執行順序時,用來標記轉移的目的地,即作轉移指令的運算元。
符號定義語句
在源程式設計中,使用符號定義語句可以將常數或表示式等內容用某個指定的符號來表示。在8086/8088組合語言中有兩種符號定義語句。
等值語句
語句格式:符號名 EQU 表示式
功能:用符號名來表示EQU右邊的表示式。後面的程式中一旦出現該符號名,彙編程式將把它替換成該表示式。表示式可以是任何形式,常見的有以下幾種情況。
1.常數或數值表示式
2.地址表示式
3.變數、暫存器名或指令助記符
同一源程式中,同一符號不能用EQU定義多次。
等號語句
格式:符號名=表示式
可以對一個符號進行多次定義。 不能為助記符定義別名
等值語句與等號語句都不會為符號分配儲存單元。因此所定義的符號沒有段、偏移量和型別等屬性。
表示式與運算子
任何表示式的值在程式被彙編的過程中進行計算確定,而不是到程式執行時才計算。
一、算術運算子
1.運算子“+”和“-”也可作單目運算子,表示數的正負。
2.使用“+”、“-”、“*”、和“/”運算子時,參加運算的數和運算結果都是整數。
3.“/"運算為取商的整數部分,而“MOD”運算取除法運算的餘數。
4.“SHR ”和“SHL”為邏輯移位運算子
移位運算子的操作物件是某一具體的數(常數),在彙編時完成移位操作。而移位指令是對一個暫存器或儲存單元內容在程式執行時執行移位操作。
5.下標運算子“[]”具有相加的作用
一般使用格式:表示式1[表示式2]
二、邏輯運算子
1.NOT
2.AND
3.OR
4.XOR
都是按位邏輯運算。
三、關係運算符
包括:EQ(等於)、NE(不等於)、LT(小於)、LE(小於等於)、GT(大於)、GE(大於等於)
關係運算符比較的兩個表示式必須同為常數或同一邏輯段中的變數。如果是常量的比較,則按無符號數進行比較;如果是變數的比較,則比較它們的偏移量的大小。
四、數值返回運算子
它們將變數或標號的某些特徵值或儲存單元地址的一部分提取出來。
五、屬性修改運算子
這一類運算子用來對變數、標號或儲存器運算元的型別屬性進行修改或指定。
六、運算子的優先順序
- 先執行優先級別高的運算,再算較低級別運算;
- 相同優先級別的操作,按照在表示式中的順序,從左到右進行;
- 可以用圓括號改變運算的順序。
程式的段結構
8086/8088在管理記憶體時,按照邏輯段進行劃分, 不同的邏輯段可以用來存放不同目的的資料。在程式中使用四個段暫存器CS,DS,ES和SS來訪問它們。在源程式設計時,使用偽指令來定義和使用這些邏輯段。
段定義偽指令
偽指令SEGMENT和ENDS用於定義一個邏輯段。使用時必須配對,分別表示定義的開始與結束
段定址偽指令
段定址偽指令ASSUME的作用是告訴彙編程式,在處理源程式時,定義的段與哪個暫存器關聯。ASSUME並不設定各個段暫存器的具體內容,段暫存器的值是在程式執行時設定的
段暫存器的裝入
段暫存器的初值(段基值)裝入需要用程式的方法來實現。四個段暫存器的裝入方法略有不同
DS和ES的裝入:
在程式中,使用資料傳送語句來實現對DS和ES的裝入
SS的裝入:
在段定義偽指令的組合型別項中,使用STACK引數,並在段定址偽指令ASSUME語句中把該段與SS段暫存器關聯,如果在段定義偽指令的組合型別中,未使用STACK引數,或者是在程式中要調換到另一個堆疊,這時,可以使用類似於DS和ES的裝入方法
CS的裝入:
CPU在執行指令之前根據CS和IP的內容來從記憶體中提取指令,即必須在程式執行之前裝入CS和IP的值。因此,CS和IP的初始值就不能用可執行語句來裝入