假設乙個處理器與儲存器相連,儲存器中存放著一些指令。這些指令通過處理器發出的定址訊號被載入到處理器中,這個過程稱為取指令。
下面通過簡單的加法運算,來看一下指令和資料是怎麼儲存在ram中的。
如果要將76abh和236ch相加,先將這兩個加數儲存在資料ram中如下:
這是兩個加數儲存在ram中的位置,為了方便運算,將高低位元組分別儲存。可以看到這個ram儲存器的最小儲存單元是乙個位元組,位址是乙個16位位址,所以這個ram總共由2的16次方個小的8位ram組成:即該ram的大小為64k*8。
說完了資料的儲存情況,再看一下**的儲存情況:
實現該設計的關鍵是,將這些**輸出到3個不同的暫存器中儲存。第乙個暫存器儲存**本身,第二個暫存器儲存位址的高位元組,第三個暫存器儲存位址的低位元組。第二個和第三個暫存器的輸出構成了資料ram的乙個16位位址。
取指令在這裡就是指的定址ram,將**ram中的指令放進加法器中執行。在這裡一條指令有3個位元組,所以取每條指令需要3個時鐘週期,由於第二位和第三位暫存器中儲存的是ram中的乙個位址,所以在取到這個位址後還需要取ram的相應位址,以獲得該位址上的資料。所以乙個完整的指令週期在這裡需要4個時鐘週期。
現在我們使用的是3位元組長的指令格式,第二個位元組和第三個位元組用來指明運算元在ram中的儲存位址,為了方便,完全沒有必要取分資料ram和**ram。
例如,現在這裡有乙個算術運算:45h+a9h-8eh。如何通過指令**來描述這個運算?
從0000h到000ch都是指令**,從0010h開始的3個儲存單元,存放的是運算元據。可以看到在同乙個ram上已經完全可以描述乙個算術運算。
此時,如果需要在原來的運算結果基礎上再加上兩個數:45h+a9h-8eh+43h+2fh。
有兩種做法:
第一種就是從0000h開始,向儲存器中輸入新的指令以替換原來所有的指令,這種方式不需要懂任何腦筋,是最笨的方法,就像你從北京出發去南京旅遊,到了南京以後突然改變主意,想去上海,然後你又回到北京,買了北京到上海的票。
第二種做法利用了前面的計算結果,接著做計算,這才是正常的思路。先將000ch處的halt指令去掉,從000ch開始加上新增的兩條add指令和halt指令,可是這樣的話新增的指令會覆蓋掉從0010h處開始存放的原來的資料。於是又有另一種方法,能不能從0014h以後的某片ram區域開始,存放新的指令以及資料,這時兩片區域是不連續的:
在這裡假設從0020h開始加上新的指令以及資料,可是問題又來了,要知道程式計數器是從0000h開始順序遞增的,不會從乙個位址跳到另外乙個不相鄰的位址,要想實現跳轉的功能,得從硬體層面對計數器進行改進,這是可以實現的,具體的方法這裡就不展開敘述了。
通過對計數器的改進,我們新增了一條指令:jump指令。即加法器(此時的加法器可以理解為乙個簡易的cpu,它會去執行從ram中讀取到的指令)一執行到jump指令,改造後的計數器就會被強制輸出該jump指令後的16位位址值,**的執行不再是順序執行。
將原來000ch處的halt指令修改如下:
當pc運轉到000ch的時候,被強制輸出0020h,即**跳到0020h開始接著執行。在硬體層面上這只是乙個很小的改動,可是對於**的執行而言,是乙個很大的改進。有了jump指令,我們就可以在ram儲存空間中自由的跳轉,其實相比較於跳轉指令,我們更需要的是讓跳轉指令在某個條件下才執行。
例如,現在有乙個乘法運算:00a7h*001ch。小學生都學過,乘法運算的實質就是加法運算,即00a7h和001ch相乘的結果和把001ch(十進位制為28)個00a7h累加的結果相同。
可以看到,從0000h開始到0012h,使ram位址1004h和1005h裡面儲存的值是00a7h乘以1的結果。如果將這段區間的指令重複28次,當然可以得到想要的乘法運算結果,可是這種方法挺笨的。
如果用到之前提過的乙個新指令,就是在0012h處放置一條jump。
這樣確實能讓**不斷的重複,但是這樣會有乙個問題,0000h到0012h之間的指令會永遠重複的執行,因為沒有一條halt指令讓它停下來。這時其實我們需要的是一種條件跳轉指令,就是讓某段指令重複指定的次數,一旦達到某個值,就不重複了。要實現條件跳轉,還是先得從硬體層面進行優化:
首先新增乙個一位鎖存器,該鎖存器稱為零鎖存器,將該鎖存器與加法器和或非門連線如下:
只有當上一步操作是add、subtract、add with carry、subtract with borrow時,零鎖存器才會存住乙個數,至於這個數是0還是1,就要看上一步操作的輸出是什麼了。如果加法器的8位輸出全為0時,零鎖存器裡的值為1。如果加法器8位輸出有乙個為1,零鎖存器裡的值就是0。
現在通過對硬體的改進,新增了下面四種跳轉指令:
要實現乘法運算,我們使用乙個非零跳轉,對應的指令碼為33h。
將0012h後的指令設計如下:
這一段指令執行的操作是,將001ch(其中乙個乘數)與00ffh相加,這兩個數相加的結果其實就等同於從001ch中減一的結果,如果是第一次執行,這個結果為001bh,所以加法器的輸出不全為0,執行非零跳轉,跳轉到0000h處開始繼續執行下一輪加法運算,所以上面這段**其實起到了乙個計數的功能,記錄了當前的累加執行到哪一次了,當執行完最後一次類加,1003h裡儲存的值是0001h,加上00ffh後,結果為0,即加法器的輸出全為零,所以不會執行非零跳轉指令,而是接著執行001eh中的halt指令(注意:這個位址裡的ffh,我們既可以把它當作乙個普通的二進位制數,也可以把它當作乙個halt指令)。
還需要特別注意的是,這裡的非零跳轉中的非零指的是加法器的輸出非零,而不是指的鎖存器裡的值非零與否。如果把非零理解為零鎖存器中的值的話,邏輯就完全相反了。
至此我們通過乙個新增的指令,在加法的基礎上完成了乘法的操作。
通過不斷的硬體完善,特別是加上條件跳轉指令後,已經具備了計算機的雛形。能否控制迴圈是計算器與計算機的區別。上面演示了如何通過條件跳轉實現乘法運算,用類似的方法還能實現除法運算,甚至是開平方、取對數、三角函式等等也完全可以實現,只不過相對複雜一些。
指令與CPU的執行探微 二)
這篇部落格主要將介紹cpu硬體層面上的一些細節,但不會涉及全部的元件,只描述我個人認為重要的幾個點而已,更具體詳盡的描述要看前面那篇文章中介紹的兩本書。1 位址與定址 這在之前是個非常折磨我的問題,我實在想象不出如何根據乙個數字去尋找記憶體中相應的單元,難不成每個單元上面都貼著標籤?直到我看到下面所...
指令與CPU的執行探微(三)
本篇部落格試圖對cpu執行的細節進行一次全景式的描述,以彌補寫version1.0時的缺憾。此文以 深入理解計算機系統 的第四章 處理器體系結構 為藍本,結合從 編碼 中得到的知識,力圖把執行一條指令時,每一步中cpu到底都幹了些什麼描述清楚。由於這個執行過程與所使用的指令集密不可分,所以首先擺上 ...
怎麼執行儲存過程 微控制器執行指令過程詳解
微控制器執行程式的過程,實際上就是執行我們所編制程式的過程。即逐條指令的過程。計算機每執行一條指令都可分為三個階段進行。即取指令 分析指令 執行指令。取指令的任務是 根據程式計數器pc中的值從程式儲存器讀出現行指令,送到指令暫存器。計算機執行程式的過程實際上就是逐條指令地重複上述操作過程,直至遇到停...