- 計(jì)算機(jī)系統(tǒng)解密:從理解計(jì)算機(jī)到編寫(xiě)高效代碼
- (美)喬納森·E.斯坦哈特
- 2665字
- 2021-09-27 16:57:06
4.4 指令集
計(jì)算機(jī)在尋寶過(guò)程中在內(nèi)存中找到的“紙條”叫作指令。本節(jié)將介紹這些指令所包含的內(nèi)容。
4.4.1 指令
為了了解在CPU中可以找到什么樣的指令,以及如何為它們選擇位型,我們假設(shè)計(jì)算機(jī)中的指令為16位寬。
我們把指令分成四個(gè)字段:操作碼以及兩個(gè)操作數(shù)的地址和結(jié)果地址,如圖4-13所示。

圖4-13 三地址指令分布
將指令分成四段看起來(lái)是個(gè)好主意,但效果不太好。因?yàn)槲覀冎挥?個(gè)位的地址空間可以存放每個(gè)操作數(shù)或結(jié)果。當(dāng)只有16個(gè)地址位時(shí),要尋址較大的內(nèi)存有點(diǎn)困難。當(dāng)然也可以使指令更大,但即使使用64位寬的指令,我們也只有20位的地址,而20位的地址只能達(dá)到1兆字節(jié)的內(nèi)存。現(xiàn)代計(jì)算機(jī)有千兆字節(jié)的內(nèi)存。
另一種方法是借鑒圖3-23中的DRAM尋址技巧。可以增加一個(gè)地址擴(kuò)展寄存器,并用一條單獨(dú)的指令加載高階地址位。英特爾使用這項(xiàng)技術(shù)使其32位計(jì)算機(jī)可以訪問(wèn)超過(guò)4 GiB的內(nèi)存。英特爾稱這項(xiàng)技術(shù)為物理地址擴(kuò)展(Physical Address Extension, PAE)。當(dāng)然,加載這個(gè)寄存器需要額外的時(shí)間。如果我們使用這種方法創(chuàng)建的邊界兩邊的內(nèi)存,則需要大量的寄存器加載。
不過(guò),三地址格式不能正常工作還有一個(gè)更重要的原因:三地址格式依賴于某種神奇的、不存在的內(nèi)存形式才能讓三個(gè)不同的位置同時(shí)被尋址。圖4-14中的三個(gè)內(nèi)存塊都是相同的存儲(chǔ)設(shè)備,三地址格式不代表它包含三個(gè)地址總線和三個(gè)數(shù)據(jù)總線。

圖4-14 行不通的計(jì)算機(jī)架構(gòu)
要想使它工作,可以讓一個(gè)寄存器保存操作數(shù)A的內(nèi)容,讓另一個(gè)寄存器保存操作數(shù)B的內(nèi)容。硬件需要執(zhí)行以下操作:
1. 使用程序計(jì)數(shù)器中的地址從內(nèi)存中加載指令。
2. 使用指令中操作數(shù)A部分的地址將操作數(shù)A加載到寄存器中。
3. 使用指令中操作數(shù)B部分的地址將操作數(shù)B加載到寄存器中。
4. 使用指令結(jié)果部分的地址將結(jié)果存儲(chǔ)在內(nèi)存中。
寄存器是很復(fù)雜的硬件,如果每一個(gè)步驟都需要一個(gè)時(shí)鐘周期,那么就需要4個(gè)周期來(lái)完成這項(xiàng)工作。我們應(yīng)該從一次只能訪問(wèn)一個(gè)內(nèi)存位置這一事實(shí)出發(fā),并據(jù)此設(shè)計(jì)指令集。如果一次只處理一件事,就會(huì)有更多的地址位可用。
我們可以通過(guò)在寄存器街道再建造一個(gè)寄存器來(lái)做到上面的那一點(diǎn)。我們將這個(gè)寄存器稱為累加器(Accumulator),或者簡(jiǎn)稱A寄存器,它將保存來(lái)自ALU的結(jié)果。我們將在一個(gè)內(nèi)存位置和累加器之間執(zhí)行運(yùn)算,而不是在兩個(gè)內(nèi)存位置之間執(zhí)行運(yùn)算。當(dāng)然,我們必須添加一個(gè)存儲(chǔ)指令,將累加器的內(nèi)容存儲(chǔ)在某個(gè)內(nèi)存位置,因此其指令分布如圖4-15所示。

圖4-15 單地址指令分布
以上的思路讓我們得到了更多的地址位,但完成這件事情需要更多的指令。之前只需一條指令:
C=A+B
但現(xiàn)在需要三條指令:
累加器=A
累加器=累加器+B
C=累加器
你可能會(huì)注意到,一條指令變成了上面的三條指令,有效地使指令變大,且自相矛盾。對(duì)于這個(gè)簡(jiǎn)單的例子來(lái)說(shuō),的確是這樣的,但一般情況下并不會(huì)這樣。假設(shè)我們需要計(jì)算:
D=A+B+C
只使用一條指令無(wú)法計(jì)算出結(jié)果,即使這條指令可以訪問(wèn)三個(gè)地址。現(xiàn)在我們需要四個(gè)地址,我們必須這樣做:
中間體=A+B
D=中間體+C
如果堅(jiān)持使用12位地址,則需要40位指令來(lái)處理三個(gè)地址和操作碼。我們需要兩條這樣的指令,總共80位來(lái)計(jì)算D。使用單地址版本的指令需要4條指令,總共64位。
累加器=A
累加器=累加器+B
累加器=累加器+C
D=累加器
4.4.2 尋址方式
使用累加器可以得到12個(gè)地址位,雖然能夠?qū)ぶ? 096個(gè)字節(jié)比只能尋址16個(gè)字節(jié)好得多,但4 096個(gè)字節(jié)仍然不夠。這種尋址內(nèi)存的方式稱為直接尋址,意味著地址就是指令中給定的地址。
我們可以使用間接尋址來(lái)尋址更多的內(nèi)存。使用間接尋址可以從指令中包含的內(nèi)存位置獲取地址,而不是直接從指令本身獲取地址。例如,假設(shè)內(nèi)存位置12包含值4321,而內(nèi)存位置4321包含值345。如果使用直接尋址,會(huì)從內(nèi)存位置12加載,得到4321,而間接尋址將得到345,即位置4321包含的內(nèi)容。
這兩種尋址方法對(duì)于處理內(nèi)存來(lái)說(shuō)都很好,但有時(shí)候我們只需要得到常量。例如,如果要數(shù)到10,我們就需要使用某種方法來(lái)加載這個(gè)數(shù)字。我們可以用另一種尋址方式來(lái)實(shí)現(xiàn),稱為立即數(shù)尋址。在立即數(shù)尋址中,地址只是作為一個(gè)數(shù)字被處理。因此,前面的示例若以立即數(shù)尋址方式加載位置12將得到12。這些尋址方式對(duì)比如圖4-16所示。

圖4-16 尋址方式
顯然,直接尋址比立即數(shù)尋址慢,因?yàn)樗枰啻蝺?nèi)存訪問(wèn)。間接尋址速度更慢,因?yàn)樗枰俣嘁淮蝺?nèi)存訪問(wèn)。
4.4.3 條件碼指令
我們的CPU還缺少一些東西,比如處理?xiàng)l件碼的指令。前面提到這些條件碼是通過(guò)加法、減法和比較形成的。但是我們需要一些方法將它們?cè)O(shè)置為已知的值并查看這些值。要實(shí)現(xiàn)這點(diǎn),可以添加一個(gè)cca
指令將條件碼寄存器的內(nèi)容復(fù)制到累加器,再添加一個(gè)acc
指令將累加器的內(nèi)容復(fù)制到條件碼寄存器。
4.4.4 分支
現(xiàn)在我們有了可以做各種事情的指令,但我們只能從頭到尾執(zhí)行一系列的指令,這沒(méi)多大用處。我們希望的是程序可以作出決定并選擇部分代碼執(zhí)行。程序可以接收某些指令讓我們改變程序計(jì)數(shù)器的值。這些指令稱為分支指令,它們使程序計(jì)數(shù)器加載一個(gè)新地址。就其本身而言,分支指令并不比僅執(zhí)行一系列指令更有用。但是分支指令也并不總分支,它們會(huì)查看條件碼,并且只在滿足條件時(shí)才分支。不滿足條件時(shí),程序計(jì)數(shù)器正常遞增,然后執(zhí)行分支指令之后的指令。分支指令需要一些位來(lái)保存條件,如表4-2所示。
表4-2 分支指令的條件

有時(shí)我們需要明確地更改程序計(jì)數(shù)器的內(nèi)容。有兩個(gè)特殊的指令可以實(shí)現(xiàn)這一點(diǎn):pca
以及apc
。pca
可以將當(dāng)前的程序計(jì)數(shù)器值復(fù)制到累加器,apc
可以將累加器的內(nèi)容復(fù)制到程序計(jì)數(shù)器。
4.4.5 最終指令集
我們將所有這些特性集成到指令集中,如圖4-17所示。

圖4-17 最終指令分布
我們有三種尋址方式,這意味著需要2個(gè)位來(lái)實(shí)現(xiàn)尋址方式的選擇。未使用的第4個(gè)組合用于不涉及內(nèi)存的操作。
尋址方式和操作碼解碼成指令,如表4-3所示。注意,分支條件合并到了操作碼中。尋址方式3的操作碼用于只涉及累加器的操作。完整實(shí)現(xiàn)的一個(gè)副作用是操作碼與表4-1中的ALU不完全匹配。這并不罕見(jiàn),需要一些額外的邏輯。
表4-3 尋址方式和操作碼

“左移”和“右移”指令使用一些未使用的位作為要移位的位置數(shù)的計(jì)數(shù),如圖4-18所示。

圖4-18 移位指令分布
現(xiàn)在我們可以通過(guò)編寫(xiě)一個(gè)程序來(lái)指導(dǎo)計(jì)算機(jī)做一些事情,這個(gè)程序就是執(zhí)行某項(xiàng)任務(wù)的指令的列表。我們來(lái)計(jì)算從0到200的斐波那契(意大利數(shù)學(xué)家,1175—1250)數(shù)。斐波那契數(shù)相當(dāng)有意思,一朵花上花瓣的數(shù)目就是斐波那契數(shù)。前兩個(gè)斐波那契數(shù)是0和1,把它們加起來(lái)就得到了下一個(gè)斐波那契數(shù)。不斷地在前一個(gè)斐波那契數(shù)的基礎(chǔ)上加上一個(gè)新的斐波那契數(shù),即可得到斐波那契數(shù)序列,即0,1,1,2,3,5,8,13,…。該過(guò)程如圖4-19所示。

圖4-19 計(jì)算斐波那契數(shù)序列的程序流程圖
表4-4的短程序可實(shí)現(xiàn)這個(gè)過(guò)程。指令列按圖4-17的分布方式劃分為字段。描述中涉及的地址是十進(jìn)制數(shù)字。
表4-4 計(jì)算斐波那契數(shù)序列的機(jī)器語(yǔ)言程序

- Learning Microsoft Windows Server 2012 Dynamic Access Control
- Visual Basic .NET程序設(shè)計(jì)(第3版)
- ClickHouse性能之巔:從架構(gòu)設(shè)計(jì)解讀性能之謎
- 移動(dòng)UI設(shè)計(jì)(微課版)
- Vue.js入門(mén)與商城開(kāi)發(fā)實(shí)戰(zhàn)
- 數(shù)據(jù)結(jié)構(gòu)(Python語(yǔ)言描述)(第2版)
- 征服RIA
- Python數(shù)據(jù)分析(第2版)
- Learning Python Design Patterns(Second Edition)
- 精通Linux(第2版)
- Java EE 8 Application Development
- Visual C++開(kāi)發(fā)入行真功夫
- 零基礎(chǔ)學(xué)Kotlin之Android項(xiàng)目開(kāi)發(fā)實(shí)戰(zhàn)
- Serverless Web Applications with React and Firebase
- Python編程基礎(chǔ)教程