王爽著的《汇编语言》第3版笔记
王爽著的《匯編語言》(第3版)于2013年出版,雖然是2013年出版的,但書中部分內容感覺已過時:
(1). 基于intel 8086 CPU介紹,intel 8086是英特爾公司上個世紀生產的芯片,是16位的,早已停產;
(2). 現在PC機上的intel CPU都是intel core i5, i7等,大部分都是64位的,書中介紹的代碼段在現在的PC機上基于vs根本無法編譯,而且有些語法在現代匯編語言中應該也不在支持。
以下是對書中內容的摘記:注:基于intel 8086 CPU
1. 基礎知識:匯編語言是直接在硬件之上工作的編程語言。
1.1 機器語言:是機器指令的集合。機器指令展開來講就是一臺機器可以正確執行的命令。電子計算機的機器指令是一列二進制數字。計算機將之轉變為一列高低電平,以使計算機的電子器件受到驅動,進行運算。
上面所說的計算機指的是可以執行機器指令,進行運算的機器?,F在,在PC機中,有一個芯片來完成上面所說的計算機的功能。這個芯片就是CPU(Central Processing Unit, 中央處理單元),CPU是一種微處理器。
每一種微處理器,由于硬件設計和內部結構的不同,就需要用不同的電平脈沖來控制,使它工作。所以每一種微處理器都有自己的機器指令集,也就是機器語言。
早期的程序設計均使用機器語言。程序員們將用0、1數字編程的程序代碼打在紙帶或卡片上,1打孔,0不打孔,再將程序通過紙帶機或卡片機輸入計算機,進行運算。
1.2 匯編語言的產生:匯編語言的主體是匯編指令。匯編指令和機器指令的差別在于指令的表示方法上。匯編指令是機器指令便于記憶的書寫格式。例如:機器指令1000100111011000表示把寄存器BX的內容送到AX中。匯編指令則寫成mov ax,bx。
寄存器,簡單地講是CPU中可以存儲數據的器件,一個CPU中有多個寄存器。AX是其中一個寄存器的代號,BX是另一個寄存器的代號。
計算機能讀懂的只有機器指令,那么如何讓計算機執行程序員用匯編指令編寫的程序呢?這時,就需要有一個能夠將匯編指令轉換成機器指令的翻譯程序,這樣的程序我們稱其為編譯器。程序員用匯編語言寫出源程序,再用匯編編譯器將其編譯為機器碼,由計算機最終執行。其工作過程如下圖所示:
1.3 匯編語言的組成:匯編語言發展至今,有以下3類指令組成:
(1).匯編指令:機器碼的助記符,有對應的機器碼。
(2).偽指令:沒有對應的機器碼,由編譯器執行,計算機并不執行。
(3).其它符號:如+、-、*、/等,由編譯器識別,沒有對應的機器碼。
匯編語言的核心是匯編指令,它決定了匯編語言的特性。
1.4 存儲器:CPU是計算機的核心部件,它控制整個計算機的運作并進行計算。要想讓一個CPU工作,就必須向它提供指令和數據。指令和數據在存儲器中存放,也就是我們所說的內存。磁盤不同于內存,磁盤上的數據或程序如果不讀到內存中,就無法被CPU使用。
1.5 指令和數據:在內存或磁盤上,指令和數據沒有任何區別,都是二進制信息。CPU在工作的時候把有的信息看作指令,有的信息看作數據,為同樣的信息賦予了不同的意義。
1.6 存儲單元:存儲器被劃分成若干個存儲單元,每個存儲單元從0開始順序編號。電子計算機的最小信息單位是bit,也就是一個二進制位。8個bit組成一個Byte。微型機存儲器的存儲單元可以存儲一個Byte,即8個二進制位。微機存儲器的容量是以字節為最小單位來計算的。對于大容量的存儲器一般還用以下單位來計量容量:1KB、1MB、1GB、1TB。
1.7 CPU對存儲器的讀寫:存儲器被劃分成多個存儲單元,存儲單元從零開始順序編號。這些編號可以看作存儲單元在存儲器中的地址。CPU要從內存中讀數據,首先要指定存儲單元的地址。也就是說它要先確定它要讀取哪一個存儲單元中的數據。另外,在一臺微機中,不只有存儲器這一種器件。CPU在讀寫數據時還要指明,它要對哪一個器件進行操作,進行哪種操作,是從中讀出數據,還是向里面寫入數據。
可見,CPU要想進行數據的讀寫,必須和外部器件(標準的說法是芯片)進行下面3類信息的交互:存儲單元的地址(地址信息);器件的選擇,讀或寫的命令(控制信息);讀或寫的數據(數據信息)。
CPU是通過導線將地址、數據和控制信息傳到存儲芯片中。在計算機中專門有連接CPU和其它芯片的導線,通常稱為總線??偩€從物理上來講,就是一根根導線的集合。根據傳送信息的不同,總線從邏輯上又分為3類,地址總線、控制總線和數據總線。
1.8 地址總線:CPU是通過地址總線來指定存儲器單元的??梢姷刂房偩€上能傳送多少個不同的信息,CPU就可以對多少個存儲單元進行尋址。
一個CPU有N根地址線,則可以說這個CPU的地址總線的寬度為N。這樣的CPU最多可以尋找2的N次方個內存單元。
1.9 數據總線:CPU與內存或其它器件之間的數據傳送是通過數據總線來進行的。數據總線的寬度決定了CPU和外界的數據傳送速度。8根數據總線一次可傳送一個8位二進制數據(即一個字節)。16根數據總線一次可傳送兩個字節。
1.10 控制總線:CPU對外部器件的控制是通過控制總線來進行的。在這里控制總線是個總稱,控制總線是一些不同控制線的集合。有多少根控制總線,就意味著CPU提供了對外部器件的多少種控制。所以,控制總線的寬度決定了CPU對外部器件的控制能力。
1.11 內存地址空間(概述):舉例來講,一個CPU的地址總線的寬度為10,那么可以尋址1024個(2^10)內存單元,這1024個可尋到的內存單元就構成這個CPU的內存地址空間。
1.12 主板:在每一臺PC機中,都有一個主板,主板上有核心器件和一些主要器件,這些器件通過總線(地址總線、數據總線、控制總線)相連。這些器件有CPU、存儲器、外圍芯片組、擴展插槽等。擴展插槽上一般插有RAM內存條和各類接口卡。
1.13 接口卡:計算機系統中,所有可用程序控制其工作的設備,必須受到CPU的控制。CPU對外部設備都不能直接控制,如顯示器、音響、打印機等。直接控制這些設備進行工作的是插在擴展插槽上的接口卡。擴展插槽通過總線和CPU相連,所以接口卡也通過總線同CPU相連。CPU可以直接控制這些接口卡,從而實現CPU對外設的間接控制。簡單地講,就是CPU通過總線向接口卡發送命令,接口卡根據CPU的命令控制外設進行工作。
1.14 各類存儲器芯片:一臺PC機中,裝有多個存儲器芯片,這些存儲器芯片從物理連接上看是獨立的、不同的器件。從讀寫屬性上看分為兩類:隨機存儲器(RAM)和只讀存儲器(ROM)。隨機存儲器可讀可寫,但必須帶電存儲,關機后存儲的內容丟失;只讀存儲器只能讀取不能寫入,關機后其中的內容不丟失。這些存儲器從功能和連接上又可分為以下幾類:
(1).隨機存儲器:用于存放CPU使用的絕大部分程序和數據,主隨機存儲器一般由兩個位置上的RAM組成,裝在主板上RAM和插在擴展槽上的RAM。
(2).裝有BIOS(Basic Input/Output System, 基本輸入/輸出系統)的ROM:BIOS是由主板和各類接口卡(如顯卡、網卡等)廠商提供的軟件系統,可以通過它利用該硬件設備進行最基本的輸入輸出。在主板和某些接口卡上插有存儲相應BIOS的ROM。例如,主板上的ROM中存儲著主板的BIOS(通常稱為系統BIOS);顯卡上的ROM中存儲著顯卡的BIOS;如果網卡上裝有ROM,那其中就可以存儲網卡的BIOS。
(3).接口卡上的RAM:某些接口卡需要對大批量輸入、輸出數據進行暫時存儲,在其上裝有RAM。最典型的是顯示卡上的RAM,一般稱為顯存。顯示卡隨時將顯存中的數據向顯示器上輸出。換句話說,我們將需要顯示的內容寫入顯存,就會出現在顯示器上。
1.15 內存地址空間:各類存儲器芯片,在物理上是獨立的器件,但是在以下兩點上相同:都和CPU的總線相連;CPU對它們進行讀或寫的時候都通過控制線發出內存讀寫命令。這也就是說,CPU在操作它們的時候,把它們都當作內存來對待,把它們總的看作一個由若干存儲單元組成的邏輯存儲器,這個邏輯存儲器就是我們所說的內存地址空間。在匯編中,我們所面對的是內存地址空間。如下圖所示:所有的物理存儲器被看作一個由若干存儲單元組成的邏輯存儲器,每個物理存儲器在這個邏輯存儲器中占有一個地址段,即一段地址空間。CPU在這段地址空間中讀寫數據,實際上就是在相對應的物理存儲器中讀寫數據。
內存地址空間的大小受CPU地址總線寬度的限制。8086CPU的地址總線寬度為20,可以傳送2^20個不同的地址信息(大小從0至2^20-1)。即可以定位2^20個內存單元,則8086PC的內存地址空間大小為1MB。同理,80386CPU的地址總線寬度為32,則內存地址空間最大為4GB。不同的計算機系統的內存地址空間的分配情況是不同的。
小結:
(1).匯編指令是機器指令的助記符,同機器指令一一對應。
(2).每一種CPU都有自己的匯編指令集。
(3).CPU可以直接使用的信息在存儲器中存放。
(4).在存儲器中指令和數據沒有任何區別,都是二進制信息。
(5).存儲單元從零開始順序編號。
(6).一個存儲單元可以存儲8個bit,即8位二進制數。
(7).1Byte=8bit; 1KB=1024Byte; 1MB=1024KB; 1GB=1024MB。
(8).每一個CPU芯片都有許多管腳,這些管腳和總線相連。也可以說,這些管腳引出總線。一個CPU可以引出3種總線的寬度標志了這個CPU的不同方面的性能:地址總線的寬度決定了CPU的尋址能力;數據總線的寬度決定了CPU與其它器件進行數據傳送時的一次數據傳送量;控制總線的寬度決定了CPU對系統中其它器件的控制能力。
2. 寄存器
一個典型的CPU由運算器、控制器、寄存器等器件構成,這些器件靠內部總線相連。前一章所說的總線,相對于CPU內部來說是外部總線。內部總線實現CPU內部各個器件之間的聯系,外部總線實現CPU和主板上其它器件的聯系。簡單地說,在CPU中,運算器進行信息處理;寄存器進行信息存儲;控制器控制各種器件進行工作;內部總線連接各種器件,在它們之間進行數據的傳送。
寄存器是CPU中程序員可以用指令讀寫的部件。程序員通過改變各種寄存器中的內容來實現對CPU的控制。不同的CPU,寄存器的個數、結構是不相同的。8086CPU有14個寄存器,每個寄存器有一個名稱。這些寄存器是:AX、BX、CX、DX、SI、DI、SP、BP、IP、CS、SS、DS、ES、PSW。
2.1 通用寄存器:
8086CPU的所有寄存器都是16位的,可以存放兩個字節。AX、BX、CX、DX這4個寄存器通常用來存放一般性的數據,被稱為通用寄存器。一個16位寄存器可以存儲一個16位的數據。這4個通用寄存器都可分為兩個可獨立使用的8位寄存器來用:AX可分為AH和AL;BX可分為BH和BL;CX可分為CH和CL;DX可分為DH和DL。
2.2 字在寄存器中的存儲:
出于對兼容性的考慮,8086CPU可以一次性處理以下兩種尺寸的數據:
(1).字節:記為byte,一個字節由8個bit組成,可以存在8位寄存器中。
(2).字:記為word,一個字由兩個字節組成,這兩個字節分別稱為這個字的高位字節和低位字節。
2.3 幾條匯編指令:通過匯編指令控制CPU進行工作。
在寫一條匯編指令或一個寄存器的名稱時不區分大小寫。
在進行數據傳送或運算時,要注意指令的兩個操作對象的位數應當是一致的。
2.4 物理地址:CPU訪問內存單元時,要給出內存單元的地址。所有的內存單元構成的存儲空間是一個一維的線性空間,每一個內存單元在這個空間中都有唯一的地址,我們將這個唯一的地址稱為物理地址。
CPU通過地址總線送入存儲器的,必須是一個內存單元的物理地址。在CPU向地址總線上發出物理地址之前,必須要在內部先形成這個物理地址。不同的CPU可以有不同的形成物理地址的方式。
2.5 16位結構的CPU:
概括地講,16位結構描述了一個CPU具有下面幾方面的結構特性:
(1).運算器一次最多可以處理16位的數據;
(2).寄存器的最大寬度為16位;
(3).寄存器和運算器之間的通路為16位。
8086是16位結構的CPU,這也就是說,在8086內部,能夠一次性處理、傳輸、暫時存儲的信息的最大長度是16位的。內存單元的地址在送上地址總線之前,必須在CPU中處理、傳輸、暫時存放,對于16位CPU,能一次性處理、傳輸、暫時存儲16位的地址。
2.6 8086CPU給出物理地址的方法:
8086CPU有20位地址總線,可以傳送20位地址,達到1MB尋址能力。8086CPU又是16位結構,在內部一次性處理、傳輸、暫時存儲的地址為16位。從8086CPU的內部結構來看,如果將地址從內部簡單地發出,那么它只能送出16位的地址,表現出的尋址能力只有64KB。8086CPU采用一種在內部用兩個16位地址合成的方法來形成一個20位的物理地址,如下圖所示:
當8086CPU要讀寫內存時:
(1).CPU中的相關部件提供兩個16位的地址,一個稱為段地址,另一個稱為偏移地址;
(2).段地址和偏移地址通過內部總線送入一個稱為地址加法器的部件;
(3).地址加法器將兩個16位地址合成為一個20位的物理地址;
(4).地址加法器通過內部總線將20位物理地址送入輸入輸出控制電路;
(5).輸入輸出控制電路將20位物理地址送上地址總線;
(6).20位物理地址被地址總線傳送到存儲器。
地址加法器采用物理地址=段地址*16+偏移地址的方法用段地址和偏移地址合成物理地址。
2.7 “段地址*16+偏移地址=物理地址”的本質含義:CPU在訪問內存時,用一個基礎地址(段地址*16)和一個相對于基礎地址的偏移地址相加,給出內存單元的物理地址。更一般地說,8086CPU的這種尋址功能是”基礎地址+偏移地址=物理地址”尋址模式的一種具體實現方案。8086CPU中,段地址*16可看作是基礎地址。
2.8 段的概念:其實,內存并沒有分段,段的劃分來自于CPU,由于8086CPU用”基礎地址(段地址*16)+偏移地址=物理地址”的方式給出內存單元的物理地址,使得我們可以用段的方式來管理內存。
CPU訪問內存單元時,必須向內存提供內存單元的物理地址。8086CPU在內部用段地址和偏移地址移位相加的方法形成最終的物理地址。CPU可以用不同的段地址和偏移地址形成同一個物理地址。在8086PC機中,存儲單元的地址用兩個元素來描述,即段地址和偏移地址。
2.9 段寄存器:8086CPU在訪問內存時要由相關部件提供內存單元的段地址和偏移地址,送入地址加法器合成物理地址。段地址在8086CPU的段寄存器中存放。8086CPU有4個段寄存器:CS、DS、SS、ES。當8086CPU要訪問內存時由這4個段寄存器提供內存單元的段地址。
2.10 CS和IP:是8086CPU中兩個最關鍵的寄存器,它們指示了CPU當前要讀取指令的地址。CS為代碼段寄存器,IP為指令指針寄存器。
在8086CPU機中,任意時刻,設CS中的內容為M,IP中的內容為N,8086CPU將從內存M*16+N單元開始,讀取一條指令并執行。也可以這樣表述:8086機中,任意時刻,CPU將CS:IP指向的內容當作指令執行。
8086CPU的工作過程可以簡要描述如下:
(1).從CS:IP指向的內存單元讀取指令,讀取的指令進入指令緩沖器;
(2).IP=IP+所讀取指令的長度,從而指向下一條指令;
(3).執行指令。轉到步驟(1),重復這個過程。
在8086CPU加電啟動或復位后(即CPU剛開始工作時)CS和IP被設置為CS=FFFFH,IP=0000H,即在8086PC機剛啟動時,CPU從內存FFFF0H單元中讀取指令執行,FFFF0H單元中的指令是8086PC機開機后執行的第一條指令。
在內存中,指令和數據沒有任何區別,都是二進制信息,CPU在工作的時候把有的信息看作指令,有的信息看作數據。
2.11 修改CS、IP的指令:
在CPU中,程序員能夠用指令讀寫的部件只有寄存器,程序員可以通過改變寄存器中的內容實現對CPU的控制。CPU從何處執行指令是由CS、IP中的內容決定的,程序員可以通過改變CS、IP中的內容來控制CPU執行目標指令。
8086CPU大部分寄存器的值,都可以用mov指令來改變,mov指令被稱為傳送指令。但是,mov指令不能用于設置CS、IP的值,因為8086CPU沒有提供這樣的功能。能夠改變CS、IP的內容的指令被統稱為轉移指令,如jmp指令。
若想同時修改CS、IP的內容,可用形如”jmp 段地址:偏移地址”的指令完成。此指令的功能為:用指令中給出的段地址修改CS,偏移地址修改IP。若想僅修改IP的內容,可用形如”jmp 某一合法寄存器”的指令完成,此指令的功能為:用寄存器中的值修改IP。
2.12 代碼段:對于8086PC機,在編程時,可用根據需要,將一組內存單元定義為一個段。我們可以將長度為N(N<=64KB)的一組代碼,存在一組地址連續、起始地址為16的倍數的內存單元中,我們可以認為,這段內存是用來存放代碼的,從而定義了一個代碼段。
小結:
(1).段地址在8086CPU的寄存器中存放。當8086CPU要訪問內存時,由段寄存器提供內存單元的段地址。8086CPU有4個段寄存器,其中CS用來存放指令的段地址。
(2).CS存放指令的段地址,IP存放指令的偏移地址。8086機中,任意時刻,CPU將CS:IP指向的內容當作指令執行。
(3).8086CPU的工作過程:
A.從CS:IP指向的內存單元讀取指令,讀取的指令進入指令緩沖器;
B.IP指向下一條指令。
C.執行指令。(轉到步驟A,重復這個過程。)
(4).8086CPU提供轉移指令修改CS、IP的內容。
3. 寄存器(內存訪問)
3.1 內存中字的存儲:CPU中,用16位寄存器來存儲一個字。高8位存放高位字節,低8位存放低位字節。在內存中存儲時,由于內存單元是字節單元(一個單元存放一個字節),則一個字要用兩個地址連續的內存單元來存放,這個字的低位字節存放在低地址單元中,高位字節存放在高地址單元中。
字單元,即存放一個字型數據(16位)的內存單元,由兩個地址連續的內存單元組成。高地址內存單元中存放字型數據的高位字節,低地址內存單元中存放字型數據的低位字節。任何兩個地址連續的內存單元,N號單元和N+1號單元,可以將它們看成兩個內存單元,也可看成一個地址為N的字單元中的高位字節單元和低位字節單元。
3.2 DS和[address]:CPU要讀寫一個內存單元的時候,必須先給出這個內存單元的地址,在8086PC中,內存地址由段地址和偏移地址組成。8086CPU中有一個DS寄存器,通常用來存放要訪問數據的段地址。
mov al,[0]:mov指令中的[]說明操作對象是一個內存單元,[]中的0說明這個內存單元的偏移地址是0,它的段地址默認放在ds中,指令執行時,8086CPU會自動從ds中取出。8086CPU不支持將數據直接送入段寄存器的操作,ds是一個段寄存器,所以”mov ds,1000H”這條指令是非法的。只能用一個寄存器來進行中轉,即先將1000H送入一個一般的寄存器,如bx,再將bx中的內容送入ds。
3.3 字的傳送:因為8086CPU是16位結構,有16根數據線,所以,可以一次性傳送16位的數據,也就是說可以一次性傳送一個字。只要在mov指令中給出16位的寄存器就可以進行16位數據的傳送了。
3.5 數據段:對于8086PC機,在編程時,可以根據需要,將一組內存單元定義為一個段。我們可以將一組長度為N(N<=64KB)、地址連續、起始地址為16的倍數的內存單元當作專門存儲數據的內存空間,從而定義了一個數據段。
3.6 棧:是一種具有特殊的訪問方式的存儲空間。它的特殊性就在于,最后進入這個空間的數據,最先出去。棧有兩個基本的操作:入棧和出棧。入棧就是將一個新的元素放到棧頂,出棧就是從棧頂取出一個元素。棧頂的元素總是最后入棧,需要出棧時,又最先從棧中取出。棧的這種操作規則被稱為LIFO(Last In First Out, 后進先出)。
3.7 CPU提供的棧機制:8086CPU提供相關的指令來以棧的方式訪問內存空間。這意味著,在基于8086CPU編程的時候,可以將一段內存當作棧來使用。
8086CPU提供入棧和出棧指令,最基本的兩個是PUSH(入棧)和POP(出棧)。比如,”push ax”表示將寄存器ax中的數據送入棧中,”pop ax”表示從棧頂取出數據送入ax。8086CPU的入棧和出棧操作都是以字為單位進行的。
8086CPU中,有兩個寄存器,段寄存器SS和寄存器SP,棧頂的段地址存放在SS中,偏移地址存放在SP中。任意時刻,SS:SP指向棧頂元素。push指令和pop指令執行時,CPU從SS和SP中得到棧頂的地址。入棧時,棧頂從高地址向低地址方向增長。
3.8 棧頂超界的問題:8086CPU用SS和SP指示棧頂的地址,并提供push和pop指令實現入棧和出棧。
當棧滿的時候再使用push指令入棧,或??盏臅r候再使用pop指令出棧,都將發生棧頂超界問題。8086CPU不保證我們對棧的操作不會越界,我們在編程的時候要自己操心棧頂超界的問題。
3.9 push、pop指令:指令執行時,CPU要知道內存單元的地址,可以在push、pop指令中只給出內存單元的偏移地址,段地址在指令執行時,CPU從ds中取得。
push、pop實質上就是一種內存傳送指令,可以在寄存器和內存之間傳送數據,與mov指令不同的是,push和pop指令的訪問的內存單元的地址不是在指令中給出的,而是由SS:SP指出的。同時,push和pop指令還要改變SP中的內容。push和pop指令同mov指令不同,CPU執行mov指令只需一步操作,就是傳送,而執行push、pop指令卻需要兩步操作:執行push時,CPU的兩步操作是:先改變SP,后向SS:SP處傳送。執行pop時,CPU的兩步操作是:先讀取SS:SP處的數據,后改變SP。注意,push、pop等棧操作指令,修改的只是SP。
3.10 棧段:對于8086PC機,在編程時,可以根據需要,將一組內存單元定義為一個段。我們可以將長度為N(N<=64KB)的一組地址連續、起始地址為16的倍數的內存單元,當作??臻g來用,從而定義了一個棧段。要將SS:SP指向我們定義的棧段。
任意時刻,SS:SP指向棧頂元素,當棧為空的時候,棧中沒有元素,也就不存在棧頂元素,所以SS:SP只能指向棧的最底部單元下面的單元,該單元的地址為棧最底部的字單元的地址+2。push、pop等指令在執行的時候只修改SP,所以棧頂的變化范圍是0~FFFFH,從??盏臅r候的SP=0,一直壓棧,直到棧滿時SP=0;如果再次壓棧,棧頂將環繞,覆蓋了原來棧中的內容。所以一個棧段的容量最大為64KB。
段的綜述:我們可以將一段內存定義為一個段,用一個段地址指示段,用偏移地址訪問段內的單元。這完全是我們自己的安排。我們可以用一個段存放數據,將它定義為”數據段”;我們可以用一個段存放代碼,將它定義為”代碼段”;我們可以用一個段當作棧,將它定義為”棧段”。我們可以這樣安排,但若要讓CPU按照我們的安排來訪問這些段,就要:對于數據段,將它的段地址放在DS中,用mov、add、sub等訪問內存單元的指令時,CPU就將我們定義的數據段中的內容當作數據來訪問。對于代碼段,將它的段地址放在CS中,將段中第一條指令的偏移地址放在IP中,這樣CPU就將執行我們定義的代碼段中的指令。對于棧段,將它的段地址放在SS中,將棧頂單元的偏移地址放在SP中,這樣CPU在需要進行棧操作的時候,比如執行push、pop指令等,就將我們定義的棧段當作棧空間來用??梢?#xff0c;不管我們如何安排,CPU將內存中的某段內容當作代碼,是因CS:IP指向了那里;CPU將某段內存當作棧,是因為SS:SP指向了那里。一段內存,可以既是代碼的存儲空間,又是數據的存儲空間,還可以是棧空間,也可以什么也不是。關鍵在于CPU中寄存器的設置,即CS、IP、SS、SP、DS的指向。
小結:
(1).字在內存中存儲時,要用兩個地址連續的內存單元來存放,字的低位字節存放在低地址單元中,高位字節存放在高地址單元中。
(2).用mov指令訪問內存單元,可以在mov指令中只給出單元的偏移地址,此時,段地址默認在DS寄存器中。
(3).[address]表示一個偏移地址為address的內存單元。
(4).在內存和寄存器之間傳送字型數據時,高地址單元和高8位寄存器、低地址單元和低8位寄存器相對應。
(5).mov、add、sub是具有兩個操作對象的指令。jmp是具有一個操作對象的指令。
(6).8086CPU提供了棧操作機制,方案如下:在SS、SP中存放棧頂的段地址和偏移地址;提供入棧和出棧指令,它們根據SS:SP指示的地址,按照棧的方式訪問內存單元。
(7).push指令的執行步驟:SP=SP-2;向SS:SP指向的字單元中送入數據。
(8).pop指令的執行步驟:從SS:SP指向的字單元中讀取數據;SP=SP+2.
(9).任意時刻,SS:SP指向棧頂元素。
(10).8086CPU只記錄棧頂,??臻g的大小我們要自己管理。
(11).用棧來暫存以后需要恢復的寄存器的內容時,寄存器出棧的順序要和入棧的順序相反。
(12).push、pop實質上是一種內存傳送指令。
4. 第一個程序
4.1 一個源程序從寫出到執行的過程:
(1):編寫匯編源程序。
(2):對源程序進行編譯鏈接:使用匯編語言編譯程序對源程序文件中的源程序進行編譯,產生目標文件;再用鏈接程序對目標文件進行鏈接,生成可在操作系統中直接運行的可執行文件。
(3):執行可執行文件中的程序。
4.2 源程序
偽指令:在匯編語言源程序中,包含兩種指令,一種是匯編指令,一種是偽指令。匯編指令是有對應的機器碼的指令,可以被編譯為機器指令,最終為CPU所執行。而偽指令沒有對應的機器指令,最終不被CPU所執行。偽指令是由編譯器來執行的指令,編譯器根據偽指令來進行相關的編譯工作。
segment和ends是一對成對使用的偽指令,這是在寫可被編譯器編譯的匯編程序時,必須要用到的一對偽指令。segment和ends的功能是定義一個段,segment說明一個段開始,ends說明一個段結束。一個段必須有一個名稱來標識。一個匯編程序是由多個段組成的,這些段被用來存放代碼、數據或當作??臻g來使用。一個有意義的匯編程序至少要有一個段,這個段用來存放代碼。
end是一個匯編程序的結束標記,編譯器在編譯匯編程序的過程中,如果碰到了偽指令end,就結束對源程序的編譯。
assume:這條偽指令的含義為”假設”。它假設某一段寄存器和程序中的某一個用segment…ends定義的段相關聯。通過assume說明這種關聯,在需要的情況下,編譯程序可以將段寄存器和某一個具體的段相聯系。
標號:匯編源程序中,除了匯編指令和偽指令外,還有一些標號。一個標號指代了一個地址。
4.3 編輯源程序:可以用任意的文本編輯器來編輯源程序,只要最終將其存儲為純文本文件即可。
4.8 誰將可執行文件中的程序裝載進入內存并使它運行?在DOS中運行一個程序的時候,是由command將程序從可執行文件中加載入內存,并使其得以執行。
5. [BX]和loop指令
注:使用一個描述性的符號”()”來表示一個寄存器或一個內存單元中的內容。
[bx]和[0]有些類似,[0]表示內存單元,它的偏移地址是0,段地址在ds中。[bx]同樣也表示一個內存單元,它的偏移地址在bx中,段地址在ds中。
5.2 Loop指令:格式是:”loop 標號”,CPU執行loop指令的時候,要進行兩步操作,(1).(cx)=(cx)-1;(2).判斷cx中的值,不為零則轉至標號處執行程序,如果為零則向下執行。通常我們用loop指令來實現循環功能,cx中存放循環次數。在匯編語言中,標號代表一個地址。
用cx和loop指令相配合實現循環功能的3個要點:
(1).在cx中存放循環次數;
(2).loop指令中的標號所標識地址要在前面;
(3).要循環執行的程序段,要寫在標號和loop指令的中間。
在匯編源程序中,數據不能以字母開頭。
5.4 Debug和匯編編譯器masm對指令的不同處理:
對于masm匯編編譯器,在匯編源程序中,如果用指令訪問一個內存單元,則在指令中必須用”[…]”來表示內存單元,如果在”[]”里用一個常量idata直接給出內存單元的偏移地址,就要在”[]”的前面顯式地給出段地址所在的段寄存器。如果在”[]”里用寄存器,比如bx,間接給出內存單元的偏移地址,則段地址默認在ds中。當然,也可以顯式地給出段地址所在的段寄存器。
5.6 段前綴:指令”mov ax,[bx]”中,內存單元的偏移地址由bx給出,而段地址默認在ds中。我們可以在訪問內存單元的指令中顯式地給出內存單元的段地址所在的段寄存器。比如:”mov ax,ds:[bx]”,這些出現在訪問內存單元的指令中,用于顯式地指明內存單元的段地址的”ds:”, “cs:”, “ss:”, “es:”,在匯編語言中稱為段前綴。
6. 包含多個段的程序
6.1 在代碼段中使用數據:
end除了通知編譯器程序結束外,還可以通知編譯器程序的入口在什么地方。我們若要CPU從何處開始執行程序,只要在源程序中用”end 標號”指明就可以了。
在匯編源程序中,可以定義許多的段。
7. 更靈活的定位內存地址的方法
7.1 and和or指令:
and指令:邏輯與指令,按位進行與運算。通過該指令可將操作對象的相應位設為0,其它位不變。
or指令:邏輯或指令,按位進行或運算。通過該指令可將操作對象的相應位設為1,其它位不變。
7.3 以字符形式給出的數據:在匯編程序中,用’…’的方式指明數據是以字符的形式給出的,編譯器將把它們轉換為相對應的ASCII碼。
7.5 [bx+idata]:表示一個內存單元,它的偏移地址為(bx)+idata(bx中的數值加上idata)。
7.7 SI和DI:si和di是8086CPU中和bx功能相近的寄存器,si和di不能夠分成兩個8位寄存器來使用。
7.8 [bx+si]和[bx+di]:它們的含義相似。[bx+si]表示一個內存單元,它的偏移地址為(bx)+(si)(即bx中的數值加上si中的數值)。
7.9 [bx+si+idata]和[bx+di+idata]:它們的含義相似。[bx+si+idata]表示一個內存單元,它的偏移地址為(bx)+(si)+idata(即bx中的數值加上si中的數值再加上idata)。
7.10 不同的尋址方式的靈活應用:一般來說,在需要暫存數據的時候,我們都應該使用棧。??臻g在內存中,采用相關的指令,如push、pop等,可對其進行特殊的操作。
8. 數據處理的兩個基本問題
8.1 bx, si, di和bp:
(1).在8086CPU中,只有這4個寄存器可以用在”[…]”中來進行內存單元的尋址,如”mov ax, [bx+si]”。
(2).在[…]中,這4個寄存器可以單個出現,或只能以4種組合出現:bx和si、bx和di、bp和si、bp和di,比如”mov ax, [bp+di+idata]”。
(3).只要在[…]中使用寄存器bp,而指令中沒有顯性地給出段地址,段地址就默認在ss中。
8.2 機器指令處理的數據在什么地方:CPU內部、內存、端口
8.3 匯編語言中數據位置的表達:
(1).立即數(idata):對于直接包含在機器指令中的數據(執行前在CPU的指令緩沖器中),在匯編語言中稱為立即數,在匯編指令中直接給出,如”mov bx, 2000h”.
(2).寄存器:指令要處理的數據在寄存器中,在匯編指令中給出相應的寄存器名,如”mov ax, bx”.
(3).段地址(SA)和偏移地址(EA):指令要處理的數據在內存中,在匯編指令中可用[X]的格式給出EA,SA在某個段寄存器中。存放段地址的寄存器可以是默認的,比如:”mov ax, [bx]” 等指令,段地址默認在ds中;”mov ax, [bp]” 等指令,段地址默認在ss中。存放段地址的寄存器也可以是顯性給出的,如:”mov ax, ds:[bp]”.
8.4 尋址方式:當數據存放在內存中的時候,我們可以用多種方式來給定這個內存單元的偏移地址,這種定位內存單元的方法一般被稱為尋址方式。8086CPU有多種尋址方式,總結如下圖所示:
8.5 指令要處理的數據有多長:8086CPU的指令,可以處理兩種尺寸的數據,byte和word。所以在機器指令中要指明,指令進行的是字操作還是字節操作。
(1).通過寄存器名指明要處理的數據的尺寸,如”mov bx, ds:[0]”,”mov al, bl”.
(2).在沒有寄存器名存在的情況下,用操作符”X ptr”指明內存單元的長度,X在匯編指令中可以為word或byte,如”mov word ptr ds:[0], 1”,”inc byte ptr ds:[0]”.
(3).其它方法:有些指令默認了訪問的是字單元還是字節單元,比如,”push [1000H]”就不用指明訪問的是字單元還是字節單元,因為push指令只進行字操作。
8.7 div指令:除法,使用div做除法的時候應注意以下問題:
(1).除數:有8位和16位兩種,在一個寄存器或內存單元中。
(2).被除數:默認放在AX或DX和AX中,如果除數為8位,被除數則為16位,默認在AX中存放;如果除數為16位,被除數則為32位,在DX和AX中存放,DX存放高16位,AX存放低16位。
(3).結果:如果除數為8位,則AL存儲除法操作的商,AH存儲除法操作的余數;如果除數為16位,則AX存儲除法操作的商,DX存儲除法操作的余數。
8.8 偽指令dd:db和dw定義字節型數據和字型數據。dd是用來定義dword(double word, 雙字)型數據的。
8.9 dup:是一個操作符,在匯編語言中同db、dw、dd等一樣,也是由編譯器識別處理的符號。它是和db、dw、dd等數據定義偽指令配合使用的,用來進行數據的重復。
9. 轉移指令的原理:
可以修改IP,或同時修改CS和IP的指令統稱為轉移指令。概括地講,轉移指令就是可以控制CPU執行內存中某處代碼的指令。
8086CPU的轉移行為有以下幾類:
(1).只修改IP時,稱為段內轉移,比如:”jmp ax”.
(2).同時修改CS和IP時,稱為段間轉移,比如:”jmp 1000:0”.
由于轉移指令對IP的修改范圍不同,段內轉移又分為:短轉移和近轉移:
(1).短轉移IP的修改范圍為-128~127.
(2).近轉移IP的修改范圍為-32768~32767.
8086CPU的轉移指令分為以下幾類:無條件轉移指令(如jmp);條件轉移指令;循環指令(如loop);過程;中斷。
9.1 操作符offset:在匯編語言中是由編譯器處理的符號,它的功能是取得標號的偏移地址。
9.2 jmp指令:無條件轉移指令,可以只修改IP,也可以同時修改CS和IP。jmp指令要給出兩種信息:(1)轉移的目的地址;(2)轉移的距離(段間轉移、段內短轉移、段內近轉移)。
9.3 依據位移進行轉移的jmp指令:
jmp short 標號(轉到標號處執行指令):這種格式的jmp指令實現的是段內短轉移,它對IP的修改范圍為-128~127,也就是說,它向前轉移時可以最多越過128個字節,向后轉移可以最多越過127個字節。jmp指令中的”short”符號,說明指令進行的是短轉移。jmp指令中的”標號”是代碼段中的標號,指明了指令要轉移的目的地,轉移指令結束后,CS:IP應該指向標號處的指令。
jmp short 標號:(IP)=(IP)+8位位移,段內短轉移。
jmp near ptr 標號:(IP)=(IP)+16位位移,段內近轉移。
9.4 轉移的目的地址在指令中的jmp指令:
jmp far ptr 標號:段間轉移,又稱為遠轉移。far ptr指明了指令用標號的段地址和偏移地址修改CS和IP。
9.6 轉移地址在內存中的jmp指令:有兩種格式:
(1).jmp word ptr 內存單元地址(段內轉移):從內存單元地址處開始存放著一個字,是轉移的目的偏移地址。內存單元地址可用尋址方式的任一格式給出。
(2).jmp dword ptr 內存單元地址(段間轉移):從內存單元地址處開始存放著兩個字,高地址處的字是轉移的目的段地址,低地址處是轉移的目的偏移地址。內存單元地址可用尋址方式的任一格式給出。
9.7 jcxz指令:為有條件轉移指令,所有的有條件轉移指令都是短轉移,在對應的機器碼中包含轉移的位移,而不是目的地址。對IP的修改范圍都為:-128~127.如果(cx)=0,轉移到標號處執行,當(cx)!=0時,什么也不做(程序向下執行)。
9.8 loop指令:為循環指令,所有的循環指令都是短轉移,在對應的機器碼中包含轉移的位移,而不是目的地址。對IP的修改范圍都為:-128~127.
9.9 根據位移進行轉移的意義:”jmp short 標號”, “jmp near ptr 標號”, “ jcxz 標號”, “loop 標號”等幾種匯編指令,它們對IP的修改是根據轉移目的地址和轉移起始地址之間的位移來進行的。在它們對應的機器碼中不包含轉移的目的地址,而包含的是到目的地址的位移。
9.10 編譯器對轉移位移超界的檢測:注意,根據位移進行轉移的指令,它們的轉移范圍受到轉移位移的限制,如果在源程序中出現了轉移范圍超界的問題,在編譯的時候,編譯器將報錯。
10. CALL和RET指令:
call和ret指令都是轉移指令,它們都修改IP,或同時修改CS和IP。它們經常被共同用來實現子程序的設計。
10.1 ret和retf:ret指令用棧中的數據,修改IP的內容,從而實現近轉移。retf指令用棧中的數據,修改CS和IP的內容,從而實現遠轉移。
10.2 call指令:CPU執行call指令時,進行兩步操作:(1)將當前的IP或CS和IP壓入棧中;(2)轉移。call指令不能實現短轉移,除此之外,call指令實現轉移的方法和jmp指令的原理相同。
10.3 依據位移進行轉移的call指令:”call 標號”:將當前的IP壓棧后,轉到標號處執行指令。
10.4 轉移的目的地址在指令中的call指令:”call far ptr 標號”:實現的是段間轉移。
10.5 轉移地址在寄存器中的call指令:CPU執行”call 16位 寄存器”時,相當于進行:”push IP;jmp 16位 寄存器”。
10.6 轉移地址在內存中的call指令:有兩種格式:(1)”call word ptr 內存單元地址”:相當于進行”push IP; jmp word ptr 內存單元地址”;(2)”call dword ptr 內存單元地址”:相當于進行”push CS; push IP; jmp dword ptr 內存單元地址”。
10.7 call和ret的配合使用:call指令轉去執行子程序之前,call指令后面的指令的地址將存儲在棧中,所以可在子程序的后面使用ret指令,用棧中的數據設置IP的值,從而轉到call指令后面的代碼處繼續執行。
10.8 mul指令:是乘法指令,使用mul做乘法的時候,注意以下兩點:(1)兩個相乘的數,要么都是8位,要么都是16位。如果是8位,一個默認放在AL中,另一個放在8位寄存器或內存字節單元中;如果是16位,一個默認在AX中,另一個放在16位寄存器或內存字單元中。(2)結果:如果是8位乘法,結果默認放在AX中;如果是16位乘法,結果高位默認在DX中存放,低位在AX中放。
10.9 模塊化程序設計:利用call和ret指令,我們可以用簡潔的方法,實現多個相互聯系、功能獨立的子程序來解決一個復雜的問題。
10.10 參數和結果傳遞的問題:用寄存器來存儲參數和結果是最常使用的方法。對于存放參數的寄存器和存放結果的寄存器,調用者和子程序的讀寫操作恰恰相反:調用者將參數送入參數寄存器,從結果寄存器中取到返回值;子程序從參數寄存器中取到參數,將返回值送入結果寄存器。
10.11 批量數據的傳遞:我們將批量數據放到內存中,然后將它們所在內存空間的首地址放在寄存器中,傳遞給需要的子程序。對于具有批量數據的返回結果,也可用同樣的方法。除了用寄存器傳遞參數外,還有一種通用的方法是用棧來傳遞參數。
10.12 寄存器沖突的問題:在子程序的開始將子程序中所有用到的寄存器中的內容都保存起來,在子程序返回前再恢復。可用棧來保存寄存器中的內容。
11. 標志寄存器
CPU內部的寄存器中,有一種特殊的寄存器(對于不同的處理器,個數和結構都可能不同)具有以下3種作用:(1)用來存儲相關指令的某些執行結果;(2)用來為CPU執行相關指令提供行為依據;(3)用來控制CPU的相關工作方式。這種特殊的寄存器在8086CPU中,被稱為標志寄存器。8086CPU的標志寄存器有16位,其中存儲的信息通常被稱為程序狀態字(PSW)。標志寄存器和其它寄存器不一樣,其它寄存器是用來存放數據的,都是整個寄存器具有一個含義。而標志寄存器是按位起作用的,也就是說,它的每一位都有專門的含義,記錄特定的信息。8086CPU的標志寄存器的結構如下圖所示:標志寄存器的1、3、5、12、13、14、15位在8086CPU中沒有使用,不具有任何含義。而0、2、4、6、7、8、9、10、11位都具有特殊的含義。
11.1 ZF標志:標志寄存器的第6位是ZF,零標志位。它記錄相關指令執行后,其結果是否為0。如果結果為0,那么zf=1;如果結果不為0,那么zf=0.注意,在8086CPU的指令集中,有的指令的執行是影響標志寄存器的,比如,add、sub、mul、div、inc、or、and等,它們大都是運算指令(進行邏輯或算術運算);有的指令的執行對標志寄存器沒有影響,比如,mov、push、pop等,它們大都是傳送指令。
11.2 PF標志:標志寄存器的第2位是PF,奇偶標志位。它記錄相關指令執行后,其結果的所有bit位中1的個數是否為偶數。如果1的個數為偶數,pf=1,如果為奇數,那么pf=0.
11.3 SF標志:標志寄存器的第7位是SF,符號標志位。它記錄相關指令執行后,其結果是否為負。如果結果為負,sf=1;如果非負,sf=0.計算機中通常用補碼來表示有符號數據。計算機中的一個數據可以看作是有符號數,也可以看成是無符號數。SF標志,就是CPU對有符號數運算結果的一種記錄,它記錄數據的正負。在我們將數據當作有符號數來運算的時候,可以通過它來得知結果的正負。如果我們將數據當作無符號數來運算,SF的值則沒有意義,雖然相關的指令影響了它的值。某些指令將影響標志寄存器中的多個標記位,這些被影響的標記位比較全面地記錄了指令的執行結果,為相關的處理提供了所需的依據。比如指令”sub al,al”執行后,ZF、PF、SF等標志位都要受到影響,它們分別為:1、1、0.
11.4 CF標志:標志寄存器的第0位是CF,進位標志位。一般情況下,在進行無符號運算的時候,它記錄了運算結果的最高有效位向更高位的進位值,或從最高位的借位值。
11.5 OF標志:在進行有符號數運算的時候,如結果超過了機器所能表示的范圍稱為溢出。標志寄存器的第11位是OF,溢出標志位。一般情況下,OF記錄了有符號數運算的結果是否發生了溢出。如果發生溢出,OF=1;如果沒有,OF=0.一定要注意CF和OF的區別:CF是對無符號數運算有意義的標志位,而OF是對有符號數運算有意義的標志位。
11.6 adc指令:是帶進位加法的指令,它利用了CF位上記錄的進位值。adc指令比add指令多加了一個CF的值。adc指令和add指令相配合就可以對更大的數據進行加法運算。
11.7 sbb指令:是帶借位減法指令,它利用了CF位上記錄的借位值。sbb指令執行后,將對CF進行設置。利用sbb指令可以對任意大的數據進行減法運算。
11.8 cmp指令:比較指令,相當于減法指令,只是不保存結果。cmp指令執行后,將對標志寄存器產生影響。其它相關指令通過識別這些被影響的標志寄存器位來得知比較結果。同add、sub指令一樣,CPU在執行cmp指令的時候,也包含兩種含義:進行無符號數運算和進行有符號數運算。
11.9 檢測比較結果的條件轉移指令:”轉移”指的是它能夠修改IP,而”條件”指的是它可以根據某種條件,決定是否修改IP。比如,jcxz就是一個條件轉移指令,它可以檢測cx中的數值,如果(cx)=0,就修改IP,否則什么也不做。所有條件轉移指令的轉移位移都是[-128, 127]。除了jcxz之外,CPU還提供了其它條件轉移指令,大多數條件轉移指令都檢測標志寄存器的相關標志位,根據檢測的結果來決定是否修改IP。這些條件轉移指令通常都和cmp相配合使用,就好像call和ret指令通常相配合使用一樣。
因為cmp指令可以同時進行兩種比較,無符號數比較和有符號數比較,所以根據cmp指令的比較結果進行轉移的指令也分為兩種,即根據無符號數的比較結果進行轉移的條件轉移指令(它們檢測zf、cf的值)和根據有符號數的比較結果進行轉移的條件轉移指令(它們檢測sf、of和zf的值)。
常用的根據無符號數的比較結果進行轉移的條件轉移指令有:je、jne、jb、jnb、ja、jna。根據有符號數的比較結果進行轉移的條件轉移指令的工作原理和無符號的相同,只是檢測了不同的標志位。
11.10 DF標志和串傳送指令:標志寄存器的第10位是DF,方向標志位。在串處理指令中,控制每次操作后si、di的增減。df=0,每次操作后si、di遞增;df=1,每次操作后si、di遞減。
movsb指令:是將ds:si指向的內存單元中的字節送入es:di中,然后根據標志寄存器df位的值,將si和di遞增或遞減。
movsw指令:是將ds:si指向的內存單元中的字送入es:di中,然后根據標志寄存器df位的值,將si和di遞增2或遞減2。
movsb和movsw進行的是串傳送操作中的一個步驟,一般來說,movsb和movsw都和rep配合使用。
8086CPU提供兩條指令對df位進行設置:
(1)cld指令:將標志寄存器的df位置0.
(2)std指令:將標志寄存器的df位置1.
11.11 pushf和popf:pushf的功能是將標志寄存器的值壓棧,而popf是從棧中彈出數據,送入標志寄存器中。pushf和popf為直接訪問標志寄存器提供了一種方法。
12 內中斷
任何一個通用的CPU,比如8086,都具備一種能力,可以在執行完當前正在執行的指令之后,檢測到從CPU外部發送過來的或內部產生的一種特殊信息,并且可以立即對所接收到的信息進行處理。這種特殊的信息,我們可以稱其為:中斷信息。中斷的意思是指,CPU不再接著(剛執行完的指令)向下執行,而是轉去處理這個特殊信息。中斷信息可以來自CPU的內部和外部。
12.1 內中斷的產生:對于8086CPU,當CPU內部有下面的情況發生的時候,將產生相應的中斷信息:(1).除法錯誤,比如,執行div指令產生的除法溢出;(2).單步執行;(3).執行into指令;(4).執行int指令。
8086CPU用稱為中斷類型碼的數據來標識中斷信息的來源。中斷類型碼為一個字節型數據,可以表示256種中斷信息的來源。
12.2 中斷處理程序:CPU收到中斷信息后,需要對中斷信息進行處理。而如何對中斷信息進行處理,可以由我們編程決定。我們編寫的,用來處理中斷信息的程序被稱為中斷處理程序。
12.3 中斷向量表:CPU用8位的中斷類型碼通過中斷向量表找到相應的中斷處理程序的入口地址。中斷向量表就是中斷向量的列表。所謂中斷向量,就是中斷處理程序的入口地址。展開來講,中斷向量表,就是中斷處理程序入口地址的列表。中斷向量表在內存中保存,其中存放著256個中斷源所對應的中斷處理程序的入口。CPU只要知道了中斷類型碼,就可以將中斷類型碼作為中斷向量表的表項號,定位相應的表項,從而得到中斷處理程序的入口地址。
中斷向量表在內存中存放,對于8086PC機,中斷向量表指定放在內存地址0處。從內存0000:0000到0000:03FF的1024個單元中存放著中斷向量表。在中斷向量表中,一個表項存放一個中斷向量,也就是一個中斷處理程序的入口地址,對于8086CPU,這個入口地址包括段地址和偏移地址,所以一個表項占兩個字,高地址字存放段地址,低地址字存放偏移地址。
12.4 中斷過程:可以用中斷類型碼,在中斷向量表中找到中斷處理程序的入口。找到這個入口地址的最終目的是用它設置CS和IP,使CPU執行中斷處理程序。用中斷類型碼找到中斷向量,并用它設置CS和IP,這個工作是由CPU的硬件自動完成的。CPU硬件完成這個工作的過程被稱為中斷過程。CPU收到中斷信息后,要對中斷信息進行處理,首先將引發中斷過程。硬件在完成中斷過程后,CS:IP將指向中斷處理程序的入口,CPU開始執行中斷處理程序。
8086CPU在收到中斷信息后,所引發的中斷過程:
(1)(從中斷信息中)取得中斷類型碼;
(2)標志寄存器的值入棧(因為在中斷過程中要改變標志寄存器的值,所以先將其保存在棧中);
(3)設置標志寄存器的第8位TF和第9位IF的值為0;
(4)CS的內容入棧;
(5)IP的內容入棧;
(6)從內存地址為中斷類型碼*4和中斷類型碼*4+2的兩個字單元中讀取中斷處理程序的入口地址設置IP和CS。
12.5 中斷處理程序和iret指令:CPU隨時都可能執行中斷處理程序,所以中斷處理程序必須一直存儲在內存某段空間之中。而中斷處理程序的入口地址,即中斷向量,必須存儲在對應的中斷向量表表項中。
iret指令通常和硬件自動完成的中斷過程配合使用。在中斷過程中,寄存器入棧的順序是標志寄存器、CS、IP,而iret的出棧順序是IP、CS、標志寄存器,剛好和其相對應,實現了用執行中斷處理程序前的CPU現場恢復標志寄存器和CS、IP的工作。iret指令執行后,CPU回到執行中斷處理程序前的執行點繼續執行程序。
12.6 除法錯誤中斷的處理:當CPU執行div等除法指令的時候,如果發生了除法溢出錯誤,將產生中斷類型碼為0的中斷信息,CPU將檢測到這個信息,然后引發中斷過程,轉去執行0號中斷所對應的中斷處理程序。
匯編編譯器可以處理表達式。
12.11 單步中斷:基本上,CPU在執行完一條指令之后,如果檢測到標志寄存器的TF位為1,則產生單步中斷,引發中斷過程。單步中斷的中斷類型碼為1。CPU提供單步中斷功能的原因就是,為單步跟蹤程序的執行過程提供了實現機制。
12.12 響應中斷的特殊情況:一般情況下,CPU在執行完當前指令后,如果檢測到中斷信息,就響應中斷,引發中斷過程??墒?#xff0c;在有些情況下,CPU在執行完當前指令后,即便是發生中斷,也不會響應。
13 int指令
13.1 int指定:格式為”int n”,n為中斷類型碼,它的功能是引發中斷過程。CPU執行int n指令,相當于引發一個n號中斷的中斷過程??梢栽诔绦蛑惺褂胕nt指令調用任何一個中斷的中斷處理程序。int指令的最終功能和call指令相似,都是調用一段程序。
14. 端口
各種存儲器都和CPU的地址線、數據線、控制線相連。CPU在操控它們的時候,把它們都當作內存來對待,把它們總地看做一個由若干存儲單元組成的邏輯存儲器,這個邏輯存儲器我們稱其為內存地址空間。在PC機系統中,和CPU通過總線相連的芯片除各種存儲器外,還有以下3種芯片:(1)各種接口卡(比如網卡、顯卡)上的接口芯片,它們控制接口卡進行工作;(2)主板上的接口芯片,CPU通過它們對部分外設進行訪問;(3)其它芯片,用來存儲相關的系統信息,或進行相關的輸入輸出處理。在這些芯片中,都有一組可以由CPU讀寫的寄存器。這些寄存器,它們在物理上可能處于不同的芯片中,但是它們在以下兩點上相同:(1)都和CPU的總線相連,當然這些連接是通過它們所在的芯片進行的;(2)CPU對它們進行讀或寫的時候都通過控制線向它們所在的芯片發出端口讀寫命令。可見,從CPU的角度,將這些寄存器都當作端口,對它們進行統一編址,從而建立了一個統一的端口地址空間。每一個端口在地址空間中都有一個地址。
14.1 端口的讀寫:在訪問端口的時候,CPU通過端口地址來定位端口。因為端口所在的芯片和CPU通過總線相連,所以,端口地址和內存地址一樣,通過地址總線來傳送。在PC系統中,CPU最多可以定位64KB個不同的端口,則端口的地址的范圍為0~65535.
對端口的讀寫不能用mov、push、pop等內存讀寫指令。端口的讀寫指令只有兩條:in和out,分別用于從端口讀取數據和往端口寫入數據。注意,在in和out指令中,只能使用ax或al來存放從端口中讀入的數據或要發送到端口中的數據。訪問8位端口時用al,訪問16位端口時用ax。
14.3 shl和shr指令:邏輯移位指令。
shl是邏輯左移指令,它的功能為:(1)將一個寄存器或內存單元中的數據向左移位;(2)將最后移出的一位寫入CF中;(3)最低位用0補充。如果移動位數大于1時,必須將移動位數放在cl中。
shr是邏輯右移指令,它和shl所進行的操作剛和相反:(1)將一個寄存器或內存單元中的數據向右移位;(2)將最后移出的一位寫入CF中;(3)最高位用0補充。如果移動位數大于1時,必須將移動位數放在cl中。
15. 外中斷
15.1 接口芯片和端口:外設的輸入不直接送入內存和CPU,而是送入相關的接口芯片的端口中;CPU向外設的輸出也不是直接送入外設,而是先送入端口中,再由相關的芯片送到外設。CPU還可以向外設輸出控制命令,而這些控制命令也是先送到相關芯片的端口中,然后再由相關的芯片根據命令對外設實施控制??梢?#xff0c;CPU通過端口和外部設備進行聯系。
15.2 外中斷信息:當CPU外部有需要處理的事情發生的時候,比如說,外設的輸入到達,相關芯片將向CPU發出相應的中斷信息。CPU在執行完當前指令后,可以檢測到發送過來的中斷信息,引發中斷過程,處理外設的輸入。在PC系統中,外中斷源一共有以下兩類:可屏蔽中斷、不可屏蔽中斷。
(1).可屏蔽中斷:是CPU可以不響應的外中斷。CPU是否響應可屏蔽中斷,要看標志寄存器的IF位的設置。當CPU檢測到可屏蔽中斷信息時,如果IF=1,則CPU在執行完當前指令后響應中斷,引發中斷過程;如果IF=0,則不響應可屏蔽中斷。
(2).不可屏蔽中斷:是CPU必須響應的外中斷。當CPU檢測到不可屏蔽中斷信息時,則在執行完當前指令后,立即響應,引發中斷過程。
指令系統總結:8086CPU提供以下幾大類指令:
(1). 數據傳送指令:比如mov、push、pop、pushf、popf、xchg等都是數據傳送指令,這些指令實現寄存器和內存、寄存器和寄存器之間的單個數據傳送。
(2). 算術運算指令:比如add、sub、adc、sbb、inc、dec、cmp、imul、idiv等都是算術運算指令,這些指令實現寄存器和內存中的數據的算數運算。它們的執行結果影響標志寄存器的sf、zf、of、cf、pf、af位。
(3). 邏輯指令:比如and、or、not、xor、shl、shr、sal、sar、rol、ror、rcl、rcr等都是邏輯指令。除了not指令外,它們的執行結果都影響標志寄存器的相關標志位。
(4). 轉移指令:可以修改IP,或同時修改CS和IP的指令統稱為轉移指令。轉移指令分為以下幾類:
無條件轉移指令,比如jmp;
條件轉移指令,比如jcxz、je、jb、ja、jnb、jna等;
循環指令,比如loop;
過程,比如call、ret、retf;
中斷,比如int、iret;
(5). 處理機控制指令:這些指令對標志寄存器或其它處理機狀態進行設置,比如cld、std、cli、sti、nop、clc、cmc、stc、hlt、wait、esc、lock等都是處理機控制指令。
(6). 串處理指令:這些指令對內存中的批量數據進行處理,比如movsb、movsw、cmps、scas、lods、stos等。若要使用這些指令方便地進行批量數據的處理,則需要和rep、repe、repne等前綴指令配合使用。
16. 直接定址表
16.1 描述了單元長度的標號:數據標號
16.2 在其它段中使用數據標號:注意,在后面加有”:”的地址標號,只能在代碼段中使用,不能在其它段中使用。
16.3 直接定址表:可以通過依據數據,直接計算出所要找的元素的位置的表。
16.4 程序入口地址的直接定址表:可以在直接定址表中存儲子程序的地址,從而方便地實現不同子程序的調用。
17. 使用BIOS進行鍵盤輸入和磁盤讀寫
17.2 使用int 16h中斷例程讀取鍵盤緩沖區:BIOS的”int 9”中斷例程和”int 16h”中斷例程是一對相互配合的程序,int 9中斷例程向鍵盤緩沖區中寫入,int 16h中斷例程從緩沖區中讀出。它們寫入和讀出的時機不同,int 9中斷例程是在有鍵按下的時候向鍵盤緩沖區中寫入數據;而int 16h中斷例程是在應用程序對其進行調用的時候,將數據從鍵盤緩沖區中讀出,并將已讀取的鍵盤輸入從緩沖區中刪除。
GitHub:https://github.com/fengbingchun/CUDA_Test
?
?
總結
以上是生活随笔為你收集整理的王爽著的《汇编语言》第3版笔记的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Ubuntu上Vim安装NERDTree
- 下一篇: 汇编程序设计与计算机体系结构软件工程师教