<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          匯編,yyds ?。。?/h1>

          共 9616字,需瀏覽 20分鐘

           ·

          2021-07-07 14:35

          點擊藍(lán)色“程序員cxuan ”關(guān)注我喲

          加個“星標(biāo)”,歡迎來撩


          這是程序員cxuan的第41期原創(chuàng)分享


          下面我們就來介紹一下關(guān)于寄存器的相關(guān)內(nèi)容。我們知道,寄存器是 CPU 內(nèi)部的構(gòu)造,它主要用于信息的存儲。除此之外,CPU 內(nèi)部還有運算器,負(fù)責(zé)處理數(shù)據(jù);控制器控制其他組件;外部總線連接 CPU 和各種部件,進行數(shù)據(jù)傳輸;內(nèi)部總線負(fù)責(zé) CPU 內(nèi)部各種組件的數(shù)據(jù)處理。

          那么對于我們所了解的匯編語言來說,我們的主要關(guān)注點就是 寄存器。

          為什么會出現(xiàn)寄存器?因為我們知道,程序在內(nèi)存中裝載,由 CPU 來運行,CPU 的主要職責(zé)就是用來處理數(shù)據(jù)。那么這個過程勢必涉及到從存儲器中讀取和寫入數(shù)據(jù),因為它涉及通過控制總線發(fā)送數(shù)據(jù)請求并進入存儲器存儲單元,通過同一通道獲取數(shù)據(jù),這個過程非常的繁瑣并且會涉及到大量的內(nèi)存占用,而且有一些常用的內(nèi)存頁存在,其實是沒有必要的,因此出現(xiàn)了寄存器,存儲在 CPU 內(nèi)部。

          認(rèn)識寄存器

          寄存器的官方叫法有很多,Wiki 上面的叫法是 Processing Register, 也可以稱為 CPU Register,計算機中經(jīng)常有一個東西多種叫法的情況,反正你知道都說的是寄存器就可以了。

          認(rèn)識寄存器之前,我們首先先來看一下 CPU 內(nèi)部的構(gòu)造。

          CPU 從邏輯上可以分為 3 個模塊,分別是控制單元、運算單元和存儲單元,這三部分由 CPU 內(nèi)部總線連接起來。

          幾乎所有的馮·諾伊曼型計算機的 CPU,其工作都可以分為5個階段:「取指令、指令譯碼、執(zhí)行指令、訪存取數(shù)、結(jié)果寫回」。

          • 取指令階段是將內(nèi)存中的指令讀取到 CPU 中寄存器的過程,程序寄存器用于存儲下一條指令所在的地址
          • 指令譯碼階段,在取指令完成后,立馬進入指令譯碼階段,在指令譯碼階段,指令譯碼器按照預(yù)定的指令格式,對取回的指令進行拆分和解釋,識別區(qū)分出不同的指令類別以及各種獲取操作數(shù)的方法。
          • 執(zhí)行指令階段,譯碼完成后,就需要執(zhí)行這一條指令了,此階段的任務(wù)是完成指令所規(guī)定的各種操作,具體實現(xiàn)指令的功能。
          • 訪問取數(shù)階段,根據(jù)指令的需要,有可能需要從內(nèi)存中提取數(shù)據(jù),此階段的任務(wù)是:根據(jù)指令地址碼,得到操作數(shù)在主存中的地址,并從主存中讀取該操作數(shù)用于運算。
          • 結(jié)果寫回階段,作為最后一個階段,結(jié)果寫回(Write Back,WB)階段把執(zhí)行指令階段的運行結(jié)果數(shù)據(jù)寫回到 CPU 的內(nèi)部寄存器中,以便被后續(xù)的指令快速地存??;

          計算機架構(gòu)中的寄存器

          寄存器是一塊速度非??斓挠嬎銠C內(nèi)存,下面是現(xiàn)代計算機中具有存儲功能的部件比對,可以看到,寄存器的速度是最快的,同時也是造價最高昂的。

          我們以 intel 8086 處理器為例來進行探討,8086 處理器是 x86 架構(gòu)的前身。在 8086 后面又衍生出來了 8088 。

          在 8086 CPU 中,地址總線達(dá)到 20 根,因此最大尋址能力是 2^20 次冪也就是 1MB 的尋址能力,8088 也是如此。

          在 8086 架構(gòu)中,所有的內(nèi)部寄存器、內(nèi)部以及外部總線都是 16 位寬,可以存儲兩個字節(jié),因為是完全的 16 位微處理器。8086 處理器有 14 個寄存器,每個寄存器都有一個特有的名稱,即

          「AX,BX,CX,DX,SP,BP,SI,DI,IP,F(xiàn)LAG,CS,DS,SS,ES」

          這 14 個寄存器有可能進行具體的劃分,按照功能可以分為三種

          • 通用寄存器
          • 控制寄存器
          • 段寄存器

          下面我們分別介紹一下這幾種寄存器

          通用寄存器

          通用寄存器主要有四種 ,即 「AX、BX、CX、DX」 同樣的,這四個寄存器也是 16 位的,能存放兩個字節(jié)。AX、BX、CX、DX 這四個寄存器一般用來存放數(shù)據(jù),也被稱為 數(shù)據(jù)寄存器。它們的結(jié)構(gòu)如下

          8086 CPU 的上一代寄存器是 8080 ,它是一類 8 位的 CPU,為了保證兼容性,8086 在 8080 上做了很小的修改,8086 中的通用寄存器 AX、BX、CX、DX 都可以獨立使用兩個 8 位寄存器來使用。

          在細(xì)節(jié)方面,AX、BX、CX、DX 可以再向下進行劃分

          • AX(Accumulator Register) :累加寄存器,它主要用于輸入/輸出和大規(guī)模的指令運算。
          • BX(Base Register):基址寄存器,用來存儲基礎(chǔ)訪問地址
          • CX(Count Register):計數(shù)寄存器,CX 寄存器在迭代的操作中會循環(huán)計數(shù)
          • DX(data Register):數(shù)據(jù)寄存器,它也用于輸入/輸出操作。它還與 AX 寄存器以及 DX 一起使用,用于涉及大數(shù)值的乘法和除法運算。

          這四種寄存器可以分為上半部分和下半部分,用作八個 8 位數(shù)據(jù)寄存器

          • 「AX 寄存器可以分為兩個獨立的 8 位的 AH 和 AL 寄存器;」
          • 「BX 寄存器可以分為兩個獨立的 8 位的 BH 和 BL 寄存器;」
          • 「CX 寄存器可以分為兩個獨立的 8 位的 CH 和 CL 寄存器;」
          • 「DX 寄存器可以分為兩個獨立的 8 位的 DH 和 DL 寄存器;」

          除了上面 AX、BX、CX、DX 寄存器以外,其他寄存器均不可以分為兩個獨立的 8 位寄存器

          如下圖所示。

          合起來就是

          AX 的低位(0 - 7)位構(gòu)成了 AL 寄存器,高 8 位(8 - 15)位構(gòu)成了 AH 寄存器。AH 和 AL 寄存器是可以使用的 8 位寄存器,其他同理。

          在認(rèn)識了寄存器之后,我們通過一個示例來看一下數(shù)據(jù)的具體存儲方式。

          比如數(shù)據(jù) 19 ,它在 16 位存儲器中所存儲的表示如下

          寄存器的存儲方式是先存儲低位,如果低位滿足不了就存儲高位,如果低位能夠滿足,高位用 0 補全,在其他低位能滿足的情況下,其余位也用 0 補全。

          8086 CPU 可以一次存儲兩種類型的數(shù)據(jù)

          • 字節(jié)(byte):一個字節(jié)由 8 bit 組成,這是一種恒定不變的存儲方式
          • 字(word):字是由指令集或處理器硬件作為單元處理的固定大小的數(shù)據(jù),對于 intel 來說,一個字長就是兩個字節(jié),字是計算機一個非常重要的特征,針對不同的指令集架構(gòu)來說,計算機一次處理的數(shù)據(jù)也是不同的。也就是說,針對不同指令集的機器,一次能處理不用的字長,有字、雙字(32位)、四字(64位)等。

          AX 寄存器

          我們上面探討過,AX 的另外一個名字叫做累加寄存器或者簡稱為累加器,其可以分為 2 個獨立的 8 位寄存器 AH 和 AL;在編寫匯編程序中,AX 寄存器可以說是使用頻率最高的寄存器。

          下面是幾段匯編代碼

          mov ax,20   /* 將 20 送入寄存器 AX*/
          mov ah,80 /* 將 80 送入寄存器 AH*/
          add ax,10 /* 將寄存器 AX 中的數(shù)值加上 8 */
          ?

          這里注意下:上面代碼中出現(xiàn)的是 ax、ah ,而注釋中確是 AX、AH ,其實含義是一樣的,不區(qū)分大小寫。

          ?

          AX 相比于其他通用寄存器來說,有一點比較特殊,AX 具有一種特殊功能的使用,那就是使用 DIV 和 MUL 指令式使用。

          ?

          DIV 是 8086 CPU 中的除法指令。

          MUL 是 8086 CPU 中的乘法指令。

          ?

          BX 寄存器

          BX 被稱為數(shù)據(jù)寄存器,即表明其能夠暫存一般數(shù)據(jù)。同樣為了適應(yīng)以前的 8 位 CPU ,而可以將 BX 當(dāng)做兩個獨立的 8 位寄存器使用,即有 BH 和 BL。BX 除了具有暫存數(shù)據(jù)的功能外,還用于 尋址,即尋找物理內(nèi)存地址。BX 寄存器中存放的數(shù)據(jù)一般是用來作為偏移地址 使用的,因為偏移地址當(dāng)然是在基址地址上的偏移了。偏移地址是在段寄存器中存儲的,關(guān)于段寄存器的介紹,我們后面再說。

          CX 寄存器

          CX 也是數(shù)據(jù)寄存器,能夠暫存一般性數(shù)據(jù)。同樣為了適應(yīng)以前的 8 位 CPU ,而可以將 CX 當(dāng)做兩個獨立的 8 位寄存器使用,即有 CH 和 CL。除此之外,CX 也是有其專門的用途的,CX 中的 C 被翻譯為 Counting 也就是計數(shù)器的功能。當(dāng)在匯編指令中使用循環(huán) LOOP 指令時,可以通過 CX 來指定需要循環(huán)的次數(shù),每次執(zhí)行循環(huán) LOOP 時候,CPU 會做兩件事

          • 一件事是計數(shù)器自動減 1

          • 還有一件就是判斷 CX 中的值,如果 CX 中的值為 0 則會跳出循環(huán),而繼續(xù)執(zhí)行循環(huán)下面的指令,

            當(dāng)然如果 CX 中的值不為 0 ,則會繼續(xù)執(zhí)行循環(huán)中所指定的指令 。

          DX 寄存器

          DX 也是數(shù)據(jù)寄存器,能夠暫存一般性數(shù)據(jù)。同樣為了適應(yīng)以前的 8 位 CPU ,DX 的用途其實在前面介紹 AX 寄存器時便已經(jīng)有所介紹了,那就是支持 MUL 和 DIV 指令。同時也支持?jǐn)?shù)值溢出等。

          段寄存器

          CPU 包含四個段寄存器,用作程序指令,數(shù)據(jù)或棧的基礎(chǔ)位置。實際上,對 IBM PC 上所有內(nèi)存的引用都包含一個段寄存器作為基本位置。

          段寄存器主要包含

          • CS(Code Segment) :代碼寄存器,程序代碼的基礎(chǔ)位置
          • DS(Data Segment):數(shù)據(jù)寄存器,變量的基本位置
          • SS(Stack Segment):棧寄存器,棧的基礎(chǔ)位置
          • ES(Extra Segment):其他寄存器,內(nèi)存中變量的其他基本位置。

          索引寄存器

          索引寄存器主要包含段地址的偏移量,索引寄存器主要分為

          • BP(Base Pointer):基礎(chǔ)指針,它是棧寄存器上的偏移量,用來定位棧上變量
          • SP(Stack Pointer): 棧指針,它是棧寄存器上的偏移量,用來定位棧頂
          • SI(Source Index): 變址寄存器,用來拷貝源字符串
          • DI(Destination Index): 目標(biāo)變址寄存器,用來復(fù)制到目標(biāo)字符串

          狀態(tài)和控制寄存器

          就剩下兩種寄存器還沒聊了,這兩種寄存器是指令指針寄存器和標(biāo)志寄存器:

          • IP(Instruction Pointer):指令指針寄存器,它是從 Code Segment 代碼寄存器處的偏移來存儲執(zhí)行的下一條指令
          • FLAG : Flag 寄存器用于存儲當(dāng)前進程的狀態(tài),這些狀態(tài)有
            • 位置 (Direction):用于數(shù)據(jù)塊的傳輸方向,是向上傳輸還是向下傳輸
            • 中斷標(biāo)志位 (Interrupt) :1 - 允許;0 - 禁止
            • 陷入位 (Trap) :確定每條指令執(zhí)行完成后,CPU 是否應(yīng)該停止。1 - 開啟,0 - 關(guān)閉
            • 進位 (Carry) : 設(shè)置最后一個無符號算術(shù)運算是否帶有進位
            • 溢出 (Overflow) : 設(shè)置最后一個有符號運算是否溢出
            • 符號 (Sign) : 如果最后一次算術(shù)運算為負(fù),則設(shè)置  1 =負(fù),0 =正
            • 零位 (Zero) : 如果最后一次算術(shù)運算結(jié)果為零,1 = 零
            • 輔助進位 (Aux Carry) :用于第三位到第四位的進位
            • 奇偶校驗 (Parity) : 用于奇偶校驗

          物理地址

          我們大家都知道, CPU 訪問內(nèi)存時,需要知道訪問內(nèi)存的具體地址,內(nèi)存單元是內(nèi)存的基本單位,每一個內(nèi)存單元在內(nèi)存中都有唯一的地址,這個地址即是 物理地址。而 CPU 和內(nèi)存之間的交互有三條總線,即數(shù)據(jù)總線、控制總線和地址總線。

          CPU 通過地址總線將物理地址送入存儲器,那么 CPU 是如何形成的物理地址呢?這將是我們接下來的討論重點。

          現(xiàn)在,我們先來討論一下和 8086 CPU 有關(guān)的結(jié)構(gòu)問題。

          cxuan 和你聊了這么久,你應(yīng)該知道 8086 CPU 是 16 位的 CPU 了,那么,什么是 16 位的 CPU 呢?

          你可能大致聽過這個回答,16 位 CPU 指的是 CPU 一次能處理的數(shù)據(jù)是 16 位的,能回答這個問題代表你的底層還不錯,但是不夠全面,其實,16 位的 CPU 指的是

          • CPU 內(nèi)部的運算器一次最多能處理 16 位的數(shù)據(jù)
          ?

          運算器其實就是 ALU,運算控制單元,它是 CPU 內(nèi)部的三大核心器件之一,主要負(fù)責(zé)數(shù)據(jù)的運算。

          ?
          • 寄存器的最大寬度為 16 位
          ?

          這個寄存器的最大寬度值就是通用寄存器能處理的二進制數(shù)的最大位數(shù)

          ?
          • 寄存器和運算器之間的通路為 16 位
          ?

          這個指的是寄存器和運算器之間的總線,一次能傳輸 16 位的數(shù)據(jù)

          ?

          好了,現(xiàn)在你應(yīng)該知道為什么叫做 16 位 CPU 了吧。

          在你知道上面這個問題的答案之后,我們下面就來聊一聊如何計算物理地址。

          8086 CPU 有 20 位地址總線,每一條總線都可以傳輸一位的地址,所以 8086 CPU 可以傳送 20 位地址,也就是說,8086 CPU 可以達(dá)到 2^20 次冪的尋址能力,也就是 1MB。8086 CPU 又是 16 位的結(jié)構(gòu),從 8086 CPU 的結(jié)構(gòu)看,它只能傳輸 16 位的地址,也就是 2^16 次冪也就是 64 KB,那么它如何達(dá)到 1MB 的尋址能力呢?

          原來,8086 CPU 的內(nèi)部采用兩個 16 位地址合成的方式來傳輸一個 20 位的物理地址,如下圖所示

          敘述一下上圖描述的過程

          CPU 相關(guān)組件提供兩個地址:段地址和偏移地址,這兩個地址都是 16 位的,他們經(jīng)由地址加法器變?yōu)?20 位的物理地址,這個地址即是輸入輸出控制電路傳遞給內(nèi)存的物理地址,由此完成物理地址的轉(zhuǎn)換。

          地址加法器采用 「物理地址 = 段地址 * 16 + 偏移地址」 的方法用段地址和偏移地址合成物理地址。

          下面是地址加法器的工作流程

          其實段地址 * 16 ,就是左移 4 位。在上面的敘述中,物理地址 = 段地址 * 16 + 偏移地址,其實就是「基礎(chǔ)地址 + 偏移地址 = 物理地址」 尋址模式的一種具體實現(xiàn)方案。基礎(chǔ)地址其實就等于段地址 * 16。

          你可能不太清楚 的概念,下面我們就來探討一下。

          什么是段

          段這個概念經(jīng)常出現(xiàn)在操作系統(tǒng)中,比如在內(nèi)存管理中,操作系統(tǒng)會把不同的數(shù)據(jù)分成 來存儲,比如 「代碼段、數(shù)據(jù)段、bss 段、rodata 段」 等。

          但是這些的劃分并不是內(nèi)存干的,cxuan 告訴你是誰干的,這其實是幕后 Boss CPU 搞的,內(nèi)存當(dāng)作了聲討的對象。

          其實,內(nèi)存沒有進行分段,分段完全是由 CPU 搞的,上面聊過的通過基礎(chǔ)地址 + 偏移地址 = 物理地址的方式給出內(nèi)存單元的物理地址,使得我們可以分段管理 CPU。

          如圖所示

          這是兩個 16 KB 的程序分別被裝載進內(nèi)存的示意圖,可以看到,這兩個程序的段地址的大小都是 16380。

          ?

          這里需要注意一點, 8086 CPU 段地址的計算方式是段地址 * 16,所以,16 位的尋址能力是 2^16 次方,所以一個段的長度是 64 KB。

          ?

          段寄存器

          cxuan 在上面只是簡單為你介紹了一下段寄存器的概念,介紹的有些淺,而且介紹段寄存器不介紹段也有「不知廬山真面目」的感覺,現(xiàn)在為你詳細(xì)的介紹一下,相信看完上面的段的概念之后,段寄存器也是手到擒來。

          我們在合成物理地址的那張圖提到了 相關(guān)部件 的概念,這個相關(guān)部件其實就是段寄存器,即 「CS、DS、SS、ES」 。8086 的 CPU 在訪問內(nèi)存時,由這四個寄存器提供內(nèi)存單元的段地址。

          CS 寄存器

          要聊 CS 寄存器,那么 IP 寄存器是你繞不過去的曾經(jīng)。CS 和 IP 都是 8086 CPU 非常重要的寄存器,它們指出了 CPU 當(dāng)前需要讀取指令的地址。

          ?

          CS 的全稱是 Code Segment,即代碼寄存器;而 IP 的全稱是 Instruction Pointer ,即指令指針?,F(xiàn)在知道這兩個為什么一起出現(xiàn)了吧!

          ?

          在 8086 CPU 中,由 CS:IP 指向的內(nèi)容當(dāng)作指令執(zhí)行。如下圖所示

          說明一下上圖

          在 CPU 內(nèi)部,由 CS、IP 提供段地址,由加法器負(fù)責(zé)轉(zhuǎn)換為物理地址,輸入輸出控制電路負(fù)責(zé)輸入/輸出數(shù)據(jù),指令緩沖器負(fù)責(zé)緩沖指令,指令執(zhí)行器負(fù)責(zé)執(zhí)行指令。在內(nèi)存中有一段連續(xù)存儲的區(qū)域,區(qū)域內(nèi)部存儲的是機器碼、外面是地址和匯編指令。

          上面這幅圖的段地址和偏移地址分別是 2000 和 0000,當(dāng)這兩個地址進入地址加法器后,會由地址加法器負(fù)責(zé)將這兩個地址轉(zhuǎn)換為物理地址

          然后地址加法器負(fù)責(zé)將指令輸送到輸入輸出控制電路中

          輸入輸出控制電路將 20 位的地址總線送到內(nèi)存中。

          然后取出對應(yīng)的數(shù)據(jù),也就是 「B8、23、01」,圖中的 B8、BB 都是操作數(shù)。

          控制輸入/輸出電路會將 B8 23 01 送入指令緩存器中。

          此時這個指令就已經(jīng)具備執(zhí)行條件,此時 IP 也就是指令指針會自動增加。我們上面說到 IP 其實就是從 Code Segment 也就是 CS 處偏移的地址,也就是偏移地址。它會知道下一個需要讀取指令的地址,如下圖所示

          在這之后,指令執(zhí)行執(zhí)行取出的 B8 23 01 這條指令。

          然后下面再把 2000 和 0003 送到地址加法器中再進行后續(xù)指令的讀取。后面的指令讀取過程和我們上面探討的如出一轍,這里 cxuan 就不再贅述啦。

          通過對上面的描述,我們能總結(jié)一下 8086 CPU 的工作過程

          • 段寄存器提供段地址和偏移地址給地址加法器
          • 由地址加法器計算出物理地址通過輸入輸出控制電路將物理地址送到內(nèi)存中
          • 提取物理地址對應(yīng)的指令,經(jīng)由控制電路取回并送到指令緩存器中
          • IP 繼續(xù)指向下一條指令的地址,同時指令執(zhí)行器執(zhí)行指令緩沖器中的指令

          什么是 Code Segment

          Code Segment 即代碼段,它就是我們上面聊到就是 CS 寄存器中存儲的基礎(chǔ)地址,也就是段地址,段地址其本質(zhì)上就是一組內(nèi)存單元的地址,例如上面的 「mov ax,0123H 、mov bx, 0003H」。我們可以將長度為 N 的一組代碼,存放在一組連續(xù)地址、其實地址為 16 的倍數(shù)的內(nèi)存單元中,我們可以認(rèn)為,這段內(nèi)存就是用來存放代碼的。

          DS 寄存器

          CPU 在讀寫一個內(nèi)存單元的時候,需要知道這個內(nèi)存單元的地址。在 8086 CPU 中,有一個 DS 寄存器,通常用來存放訪問數(shù)據(jù)的段地址。如果你想要讀取一個 10000H 的數(shù)據(jù),你可能會需要下面這段代碼

          mov bx,10000H
          mov ds,bx
          mov a1,[0]

          上面這三條指令就把 10000H 讀取到了 a1 中。

          在上面匯編代碼中,mov 指令有兩種傳送方式

          • 一種是把數(shù)據(jù)直接送入寄存器
          • 一種是將一個寄存器的內(nèi)容送入另一個寄存器

          但是不僅僅如此,mov 指令還具有下面這幾種表達(dá)方式

          描述舉例
          mov 寄存器,數(shù)據(jù)比如:mov ax,8
          mov 寄存器,寄存器比如:mov ax,bx
          mov 寄存器,內(nèi)存單元比如:mov ax,[0]
          mov 內(nèi)存單元,寄存器比如:mov[0], ax
          mov 段寄存器,寄存器比如:mov ds,ax

          棧我相信大部分小伙伴已經(jīng)非常熟悉了,是一種具有特殊的訪問方式的存儲空間。它的特殊性就在于,先進入棧的元素,最后才出去,也就是我們常說的 先入后出。

          它就像一個大的收納箱,你可以往里面放相同類型的東西,比如書,最先放進收納箱的書在最下面,最后放進收納箱的書在最上面,如果你想拿書的話, 必須從最上面開始取,否則是無法取出最下面的書籍的。

          棧的數(shù)據(jù)結(jié)構(gòu)就是這樣,你把書籍壓入收納箱的操作叫做壓入(push),你把書籍從收納箱取出的操作叫做彈出(pop),它的模型圖大概是這樣

          入棧相當(dāng)于是增加操作,出棧相當(dāng)于是刪除操作,只不過叫法不一樣。棧和內(nèi)存不同,它不需要指定元素的地址。它的大概使用如下

          // 壓入數(shù)據(jù)
          Push(123);
          Push(456);
          Push(789);

          // 彈出數(shù)據(jù)
          j = Pop();
          k = Pop();
          l = Pop();

          在棧中,LIFO 方式表示棧的數(shù)組中所保存的最后面的數(shù)據(jù)(Last In)會被最先讀取出來(First Out)。

          棧和 SS 寄存器

          下面我們就通過一段匯編代碼來描述一下棧的壓入彈出的過程

          8086 CPU 提供入棧和出棧指令,最基本的兩個是 PUSH(入棧)POP(出棧)。比如 push ax 會把 ax 寄存器中的數(shù)據(jù)壓入棧中,pop ax 表示從棧頂取出數(shù)據(jù)送入 ax 寄存器中。

          ?

          這里注意一點:8086 CPU 中的入棧和出棧都是以字為單位進行的。

          ?

          我這里首先有一個初始的棧,沒有任何指令和數(shù)據(jù)。

          然后我們向棧中 push 數(shù)據(jù)后,棧中數(shù)據(jù)如下

          涉及的指令有

          mov ax,2345H
          push ax
          ?

          注意,數(shù)據(jù)會用兩個單元存放,高地址單元存放高 8 位地址,低地址單元存放低 8 位。

          ?

          再向棧中 push 數(shù)據(jù)

          其中涉及的指令有

          mov bx,0132H
          push bx

          現(xiàn)在棧中有兩條數(shù)據(jù),現(xiàn)在我們執(zhí)行出棧操作

          其中涉及的指令有

          pop ax
          /* ax = 0132H */

          再繼續(xù)取出數(shù)據(jù)

          涉及的指令有

          pop bx
          /* bx = */

          完整的 push 和 pop 過程如下

          現(xiàn)在 cxuan 問你一個問題,我們上面描述的是 10000H ~ 1000FH 這段空間來作為 push 和 pop 指令的存取單元。但是,你怎么知道這個棧單元就是 10000H ~ 1000FH 呢?也就是說,你如何選擇指定的棧單元進行存取?

          事實上,8086 CPU 有一組關(guān)于棧的寄存器 SSSP。SS 是段寄存器,它存儲的是棧的基礎(chǔ)位置,也就是棧頂?shù)奈恢?,?SP 是棧指針,它存儲的是偏移地址。在任意時刻,SS:SP 都指向棧頂元素。push 和 pop 指令執(zhí)行時,CPU 從 SS 和 SP 中得到棧頂?shù)牡刂贰?/p>

          現(xiàn)在,我們可以完整的描述一下 push 和 pop 過程了,下面 cxuan 就給你推導(dǎo)一下這個過程。

          上面這個過程主要涉及到的關(guān)鍵變化如下。

          當(dāng)使用 「PUSH」 指令向棧中壓入 1 個字節(jié)單元時,SP = SP - 1;即棧頂元素會發(fā)生變化;

          而當(dāng)使用 「PUSH」 指令向棧中壓入 2 個字節(jié)的字單元時,SP = SP – 2 ;即棧頂元素也要發(fā)生變化;

          當(dāng)使用 「POP」 指令從棧中彈出 1 個字節(jié)單元時, SP = SP + 1;即棧頂元素會發(fā)生變化;

          當(dāng)使用 「POP」 指令從棧中彈出 2 個字節(jié)單元的字單元時, SP = SP + 2 ;即棧頂元素會發(fā)生變化;

          棧頂越界問題

          現(xiàn)在我們知道,8086 CPU 可以使用 SS 和 SP 指示棧頂?shù)牡刂?,并且提?PUSH 和 POP 指令實現(xiàn)入棧和出棧,所以,你現(xiàn)在知道了如何能夠找到棧頂位置,但是你如何能保證棧頂?shù)奈恢貌粫浇缒??棧頂越界會產(chǎn)生什么影響呢?

          比如如下是一個棧頂越界的示意圖

          第一開始,SS:SP 寄存器指向了棧頂,然后向棧空間 push 一定數(shù)量的元素后,SS:SP 位于??臻g頂部,此時再向棧空間內(nèi)部 push 元素,就會出現(xiàn)棧頂越界問題。

          棧頂越界是危險的,因為我們既然將一塊區(qū)域空間安排為棧,那么在??臻g外部也可能存放了其他指令和數(shù)據(jù),這些指令和數(shù)據(jù)有可能是其他程序的,所以如此操作會讓計算機懵逼

          我們希望 8086 CPU 能自己解決問題,畢竟 8086 CPU 已經(jīng)是個成熟的 CPU 了,要學(xué)會自己解決問題了。

          然鵝(故意的),這對于 8086 CPU 來說,這可能是它一輩子的 夙愿 了,真實情況是,8086 CPU 不會保證棧頂越界問題,也就是說 8086 CPU 只會告訴你棧頂在哪,并不會知道棧空間有多大,所以需要程序員自己手動去保證。。。


            



          往期精選

          對不起,學(xué)會這些 Linux 知識后,我有點飄

          cxuan 又瞎 TM 寫了兩本 PDF。

          程序員 cxuan 的時間管理???

          哦!這該死的 C 語言!

          我工作三年了,該懂并發(fā)了(干貨)

          另外,cxuan 肝了六本 PDF,公號回復(fù) cxuan ,領(lǐng)取作者全部 PDF 。

          瀏覽 43
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報

          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  老鸭窝在线视频狂综合 | 青娱乐亚洲领先 | 北条麻妃无码精品AV | 91嫩草欧美久久久九九九 | 国产精品 乱伦 |