欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
【轉載】MIPS匯編指令

MIPS指令特點(diǎn):
1、所有指令都是32位編碼;
2、有些指令有26位供目標地址編碼;有些則只有16位。因此要想加載任何一個(gè)32位值,就得用兩個(gè)加載指令。16位的目標地址意味著(zhù),指令的跳轉或子函數的位置必須在64K以?xún)龋ㄉ舷?2K);
3、所有的動(dòng)作原理上要求必須在1個(gè)時(shí)鐘周期內完成,一個(gè)動(dòng)作一個(gè)階段;
4、有32個(gè)通用寄存器,每個(gè)寄存器32位(對32位機)或64位(對64位機);
5、本身沒(méi)有任何幫助運算判斷的標志寄存器,要實(shí)現相應的功能時(shí),是通過(guò)測試兩個(gè)寄存器是否相等來(lái)完成的;
6、所有的運算都是基于32位的,沒(méi)有對字節和對半字的運算(MIPS里,字定義為32位,半字定義為16位);
7、沒(méi)有單獨的棧指令,所有對棧的操作都是統一的內存訪(fǎng)問(wèn)方式。因為push和pop指令實(shí)際上是一個(gè)復合操作,包含對內存的寫(xiě)入和對棧指針的移動(dòng);

8、由于MIPS固定指令長(cháng)度,所以造成其編譯后的二進(jìn)制文件和內存占用空間比x86的要大,(x86平均指令長(cháng)度只有3個(gè)字節多一點(diǎn),而MIPS是4個(gè)字節);

9、尋址方式:只有一種內存尋址方式。就是基地址加一個(gè)16位的地址偏移;

10、內存中的數據訪(fǎng)問(wèn)必須嚴格對齊(至少4字節對齊)

11、跳轉指令只有26位目標地址,再加上2位的對齊位,可尋址28位的空間,即256M。意思即是說(shuō),在一個(gè)C程序內,goto語(yǔ)句只能跳轉到它之前的128M和之后的128M這個(gè)地址空間之內

12、條件分支指令只有16位跳轉地址,加上2位的對齊位,共18位尋址空間,即256K。意思即是說(shuō),在一個(gè)C程序內,if語(yǔ)句只能跳轉到它之前的128K和之后的128K這個(gè)地址空間之內;

13、MIPS默認不把子函數的返回地址(就是調用函數的受害指令地址)存放到棧中,而是存放到$31寄存器中;這對那些葉子函數有利。如果遇到嵌套的函數的話(huà),有另外的機制處理;

14、流水線(xiàn)效應。由于采用了高度的流水線(xiàn),結果產(chǎn)生了一些對程序員來(lái)說(shuō)可見(jiàn)的效應,需要注意。最重要的兩個(gè)效應就是分支延遲效應和載入延遲效應。
    a 任何一個(gè)分支跳轉語(yǔ)句后面的那條語(yǔ)句叫做分支延遲槽。實(shí)際上在程序執行到分支語(yǔ)句時(shí),當他剛把要跳轉到的地址填充好(到代碼計數器里),還沒(méi)完成本條指令,分支語(yǔ)句后面的那個(gè)指令就執行了。這是因為流水線(xiàn)效應,幾條指令同時(shí)在執行,只是處于不同的階段。具體看書(shū)上說(shuō)提前半條指令執行,沒(méi)看懂。分支延遲槽常用被利用起來(lái)完成一些參數初始化等相關(guān)工作,而不是被浪費了。
    b 載入延遲是這樣的。當執行一條從內存中載入數據的命令時(shí),是先載入到高速緩沖中,然后再取到寄存器中,這個(gè)過(guò)程相對來(lái)說(shuō)是比較慢的。在這個(gè)過(guò)程完成之前,可能已經(jīng)有幾條在流水線(xiàn)上的指令被執行了。這幾條在載入指令后被執行的指令就被稱(chēng)作載入延遲槽?,F在就有一個(gè)問(wèn)題,如果后面這幾條指令要用到載入指令所載入的那個(gè)數據怎么辦?一個(gè)通用的辦法是,把內部鎖加在數據載入過(guò)程上,這樣,當后面的指令要用這條指令時(shí),就只有先停止運行(在A(yíng)LU階段),等這條數據載入指令完成了后再開(kāi)始運行。

*MIPS指令的五級流水線(xiàn):每條指令都包含五個(gè)執行階段。
第一階段:從指令緩沖區中取指令。占一個(gè)時(shí)鐘周期;
第二階段:從指令中的源寄存器域(可能有兩個(gè))的值(為一個(gè)數字,指定$0~$31中的某一個(gè))所代表的寄存器中讀出數據。占半個(gè)時(shí)鐘周期;
第三階段:在一個(gè)時(shí)鐘周期內做一次算術(shù)或邏輯運算。占一個(gè)時(shí)鐘周期;
第四階段:指令從數據緩沖中讀取內存變量的階段。從平均來(lái)講,大約有3/4的指令在這個(gè)階段沒(méi)做什么事情,但它是指令有序性的保證(為什么是保證,我還沒(méi)看清楚?)。占一個(gè)時(shí)鐘周期;
第五階段:存儲計算結果到緩沖或內存的階段。占半個(gè)時(shí)鐘周期;
=> 所以一條指令要占用四個(gè)時(shí)鐘周期;

15、MIPS的虛擬地址內存映射空間
0x0000 0000 ~ 0x7fff ffff
用戶(hù)級空間,2GB,要經(jīng)MMU(TLB)地址翻譯。kuseg??梢钥刂埔灰?jīng)過(guò)緩沖。

0x8000 0000 ~ 0x9fff ffff
kseg0. 這塊區域為操作系統內核所占的區域,共512M。使用時(shí),不經(jīng)過(guò)地址翻譯,將最高位去掉就線(xiàn)性映射到內存的低512M(不足的就裁剪掉頂部)。但要經(jīng)過(guò)緩沖區過(guò)渡。

0xa000 0000 ~ 0xbfff ffff
kseg1. 這塊區域為系統初始化所占區域,共512M。使用時(shí),不經(jīng)過(guò)地址翻譯,也不經(jīng)過(guò)緩沖區。將最高3位去掉就線(xiàn)性映射到內存的低512M(不足的就裁剪掉頂部)。

0xc000 0000 ~ 0xffff ffff
kseg2. 這塊區域也為內核級區域。要經(jīng)過(guò)地址翻譯??梢钥刂埔灰?jīng)過(guò)緩沖。

16、MIPS的協(xié)處理器
CP0:這是MIPS芯片的配置單元。必不可少,雖然叫做協(xié)處理器,但是通常都是做在一塊芯片上。絕大部分MIPS功能的配置,緩沖的控制,異常/中斷的控制,內存管理的控制都在這里面。所以是一個(gè)完整的系統所必不可少的;

17、 MIPS的高速緩沖
MIPS一般有兩到三級緩沖,其中第一級緩沖數據和指令分開(kāi)存儲。這樣的好處是指令和數據可以同時(shí)存取,提高效率。但缺點(diǎn)是提高了復雜度。第二級緩沖和第三級緩沖(如果有的話(huà))就不再分開(kāi)存放啦。

緩沖的單元叫做緩沖行(cache line)。每一行中,有一個(gè)tag,然后后面接的是一些標志位和一些數據。緩沖行按順序線(xiàn)性排列起來(lái),就組成了整個(gè)緩沖。

cache line的索引和存取有一套完整的機制。
18、MIPS的異常機制
精確異常的概念:在運行流程中沒(méi)有任何多余效應的異常。即當異常發(fā)生時(shí),在受害指令之前的指令被完全執行,而受害指令及后面的指令還沒(méi)開(kāi)始執行(注:說(shuō)受害指令及后面的指令還沒(méi)做任何事情是不對的,實(shí)際上受害指令是處于其指令周期的第三階段剛完成,即ALU階段剛完成)。精確異常有有助于保證軟件設計上不受硬件實(shí)現的影響。

CP0中的EPC寄存器用于指向異常發(fā)生時(shí)指令跳轉前的執行位置,一般是受害指令地址。當異常時(shí),是返回這個(gè)地址繼續執行。但如果受害指令在分支延遲槽中,則會(huì )硬件自動(dòng)處理使EPC往回指一條指令,即分支指令。在重新執行分支指令時(shí),分支延遲槽中的指令會(huì )被再執行一次。

精確異常的實(shí)現對流水線(xiàn)的流暢性是有一定的影響的,如果異常太多,系統執行效率就會(huì )受到影響。

*異常又分常規異常和中斷兩類(lèi)。常規異常一般為軟件的異常,而中斷一般為硬件異常,中斷可以是芯片內部,也可以是芯片外部觸發(fā)產(chǎn)生。

異常發(fā)生時(shí),跳轉前最后被執行的指令是其MEM階段剛好被執行完的那條指令。受害指令是其ALU階段剛好執行完的那條指令。

異常發(fā)生時(shí),會(huì )跳到異常向量入口中去執行。MIPS的異常向量有點(diǎn)特殊,它一般只個(gè)2個(gè)或幾個(gè)中斷向量入口,一個(gè)入口給一般的異常使用,一個(gè)入口給 TLB miss異常使用(這樣的話(huà),可以省下計算異常類(lèi)型的時(shí)間。在這種機制幫助下,系統只用13個(gè)時(shí)鐘周期就可以把TLB重填好)。

CP0寄存器中有個(gè)模式位,SR(BEV),只要設置了,就會(huì )把異常入口點(diǎn)轉移到非緩沖內存地址空間中(kseg1)。

MIPS系統把重啟看作一個(gè)不可回歸的異常來(lái)處理。
冷啟動(dòng):CPU硬件完全被重新配置,軟件重新加載;
熱啟動(dòng):軟件完全重新初始化;

MIPS對異常的處理的哲學(xué)是給異常分配一些類(lèi)型,然后由軟件給它們定義一些優(yōu)先級,然后由同一個(gè)入口進(jìn)入異常分配程序,在分配程序中根據類(lèi)型及優(yōu)先級確定該執行哪個(gè)對應的函數。這種機制對兩個(gè)或幾個(gè)異常同時(shí)出現的情況也是適合的。

下面是當異常發(fā)生時(shí)MIPS CPU所做的事情:
a 設置EPC指向回歸的位置;
b 設置SR(EXL)強迫CPU進(jìn)入kernel態(tài),并禁止所有中斷響應。
c 設置Cause寄存器,以使軟件可以得到異常的類(lèi)型信息;還有其它一些寄存器在某些異常時(shí)會(huì )被設置;
d CPU開(kāi)始從異常入口取指令,然后以后的所有事情都交由軟件處理了。

k0和k1寄存器用于保存異常處理函數的地址。
異常處理函數執行完成后,會(huì )回到異常分配函數那去,在異常分配函數里,有一個(gè)eret指令,用于回歸原來(lái)被中斷的程序繼續執行;eret指令會(huì )原子性地把中斷響應打開(kāi)(置SR(EXL)),并把狀態(tài)級由kernel轉到user級,并返回原地址繼續執行。

19、中斷
MIPS CPU有8個(gè)獨立的中斷位(在Cause寄存器中),其中,6個(gè)為外部中斷,2個(gè)為內部中斷(可由軟件訪(fǎng)問(wèn))。一般來(lái)說(shuō),片上的時(shí)鐘計數/定時(shí)器,會(huì )連接到一個(gè)硬件位上去。

SR(IE)位控制全局中斷響應,為0的話(huà),就禁止所有中斷;
SR(EXL)和SR(ERL)位(任何一個(gè))如果置1的話(huà),會(huì )禁止中斷;
SR(IM)有8位,對應8個(gè)中斷源,要產(chǎn)生中斷,還得把這8位中相應的位置1才行;

中斷處理程序也是用通用異常入口。但有些新的CPU有變化。

*在軟件中實(shí)現中斷優(yōu)先級的方案
a 給各種中斷定優(yōu)先級;
b CPU在運行時(shí)總是處于某個(gè)優(yōu)先級(即定義一個(gè)全局變量);
c 中斷發(fā)生時(shí),只有等于高于CPU優(yōu)先級的中斷優(yōu)先級才能執行;(如果CPU優(yōu)先級處于最低,那么所有的中斷都可以執行);
d 同時(shí)有多個(gè)中斷發(fā)生時(shí),優(yōu)先執行優(yōu)先級最高的那個(gè)中斷程序;

20、大小端問(wèn)題
硬件上也有大端小端問(wèn)題,比如串口通訊,一個(gè)字節一個(gè)字節的發(fā),首先是低位先發(fā)出去。
還有顯卡的顯示,比如顯示黑白圖像,在屏幕上一個(gè)點(diǎn)對應顯存中的一位,這時(shí),這個(gè)位對應關(guān)系就是屏幕右上角那個(gè)點(diǎn)對應顯存第一個(gè)字節的7號位,即最高位。第一排第8位點(diǎn)對應第一個(gè)字節的0號位。

21、MIPS上的Linux運行情況

用戶(hù)態(tài)和核心態(tài):在用戶(hù)態(tài),不能隨意訪(fǎng)問(wèn)內核代碼和數據存放區,只能訪(fǎng)問(wèn)用戶(hù)態(tài)空間和內核允許訪(fǎng)問(wèn)(通過(guò)某種機制)的內核頁(yè)面。也不能執行CP0相關(guān)的指令。用戶(hù)態(tài)要執行內核的某些服務(wù),就得用系統調用(system_call),在系統調用的最后,是一個(gè)eret指令。

任何時(shí)候Linux都有至少一個(gè)線(xiàn)程在跑,Linux一般不禁止中斷。發(fā)生中斷時(shí),其環(huán)境是從被中斷線(xiàn)程借來(lái)的。

中斷服務(wù)程序(ISR)應該短小。

MIPS Linux系統上半地址空間只能用內核特權級訪(fǎng)問(wèn)。內核不通過(guò)TLB地址翻譯。

所有線(xiàn)程都共用相同的內核地址空間,但只有同一組線(xiàn)程才用同一個(gè)用戶(hù)地址空間(指向同一個(gè)mm_struct結構)。

如果物理內存高于512M,那么不能用kseg0和kseg1來(lái)映射高于512M的內存部分。只能用kseg2來(lái)映射。kseg2要經(jīng)過(guò)TLB。

從某個(gè)方面說(shuō),內核就是一組供異常處理函數調用的子程序。內核中,線(xiàn)程調度器就是這樣一個(gè)小的子程序。由各個(gè)線(xiàn)程(異常處理程序也可以算作一個(gè)特殊的線(xiàn)程,換他書(shū)上的說(shuō)法)調用。

MIPS Linux有異常模式,而x86上沒(méi)有這個(gè)概念。

異常要小心操作。不是僅用軟件鎖就能解決的。

21、原子操作
MIPS為支持操作系統的原子操作,特地加了一組指令 ll/sc。它們這樣來(lái)使用:

先寫(xiě)一句
atomic_block:
LL XX1, XXX2
….
sc XX1, XXX2
beq XX1, zero, automic_block
….

在ll/sc中間寫(xiě)上你要執行的代碼體,這樣就能保證寫(xiě)入的代碼體是原子執行的(不會(huì )被搶占的)。

其實(shí),LL/sc兩語(yǔ)句自身并不保證原子執行,但他耍了個(gè)花招:
用一個(gè)臨時(shí)寄存器XX1,執行LL后,把XXX2中的值載入XX1中,然后會(huì )在CPU內部置一個(gè)標志位,我們不可見(jiàn),并保存XXX2的地址,CPU會(huì )監視它。在中間的代碼體執行的過(guò)程中,如果發(fā)現XXX2的內容變了(即是別的線(xiàn)程執行了,或是某個(gè)中斷發(fā)生了),就自動(dòng)把CPU內部那個(gè)標志位清0。執行sc 時(shí),把XX1的內容(可能已經(jīng)是新值了)存入XXX2中,并返回一個(gè)值存入XX1中,如果標志位還為1,那么這個(gè)返回的值就為1;如果標志位為0,那么這個(gè)返回值就為0。為1的話(huà),就表明這對指令中間的代碼是一次性執行完成的,而不是中間受到了某些中斷,那么原子操作就成功了;為0的話(huà),就表明原子操作沒(méi)成功,執行后面beq指令時(shí),就會(huì )跳轉到ll指令重新執行,直到原子操作成功為止。

所以,我們要注意,插在LL/sc指令中間的代碼必須短小。

據經(jīng)驗,一般原子操作的循環(huán)不會(huì )超過(guò)3次。

22、系統調用 syscall
系統調用也通過(guò)異常入口進(jìn)入系統內核,選擇8號異常代碼處理函數進(jìn)行處理,進(jìn)入系統調用分配函數后,還要根據傳進(jìn)來(lái)的參數再一次分配到具體的功能函數上去。系統調用傳遞參數是在寄存器中進(jìn)行的。

系統調用號存放在v0中,參數存放在a0-a3。如果參數過(guò)多,會(huì )有另一套機制來(lái)處理。系統調用的返回值通常放在v0中。如果系統調用出錯,則會(huì )在a3中返回一個(gè)錯誤號。

23、異常入口點(diǎn)位于kseg0的底部,是硬件規定的。

24、注意:地址空間的0x0000 0000是不能用的,從0開(kāi)始的一個(gè)或多個(gè)頁(yè)不會(huì )被映射。

25、內存分頁(yè)映射有以下優(yōu)點(diǎn):
a 隱藏和保護數據;
b 分配連續的地址給程序;
c 擴展地址空間;
d 按需求載入代碼和數據(通過(guò)異常方式);
e 便于重定位;
f 代碼和數據在線(xiàn)程中共享,便于交換數據;

所有的線(xiàn)程是平等的,所有的線(xiàn)程都有自己的內存管理結構體;運行于同一地址空間的線(xiàn)程組,共享有大部分這種數據結構。在線(xiàn)程中,保存有本地址空間已經(jīng)使用的頁(yè)面的一個(gè)頁(yè)表,用來(lái)記錄每個(gè)已用的虛頁(yè)與實(shí)際物理頁(yè)的映射關(guān)系;

26、ASID是與虛擬頁(yè)高位配合使用。用于描述在TLB和Cache中的不同的線(xiàn)程,只有8位,所以最多只能同時(shí)運行256個(gè)線(xiàn)程。這個(gè)數字一般來(lái)說(shuō)是夠的。如果超過(guò)這個(gè)數目了,就要把Cache刷新了重新裝入。所以,在這點(diǎn)上,與x86是不同的。

27、MIPS Linux的內存駐留頁(yè)表結構
用的是兩級頁(yè)表,一個(gè)頁(yè)表目錄,一個(gè)頁(yè)表,頁(yè)表中的每一項是一個(gè) EntryLo0-1。
(這與x86方式類(lèi)似)。而沒(méi)有用MIPS原生設計的方案。

28、TLB的refill過(guò)程-硬件部分
a CPU先產(chǎn)生一個(gè)虛擬地址,要到這個(gè)地址所對應的物理地址上取數據(或指令)或寫(xiě)數據(或指令)。
低13位被分開(kāi)來(lái)。然后高19位成為VPN2,和當前線(xiàn)程的ASID(從EntryHi(ASID)?。┮黄鹋浜吓cTLB表中的項進(jìn)行比較。(在比較過(guò)程中,會(huì )受到PageMask和G標志位的影響)
b 如果有匹配的項,就選擇那個(gè)。虛擬地址中的第12位用于選取是用左邊的物理地址項還是用右邊的物理地址項。
然后就會(huì )考察V和D標志位,V標志位表示本頁(yè)是否有效,D表示本頁(yè)是否已經(jīng)臟了(被寫(xiě)過(guò))。
如果V=0,或D=1,就會(huì )引發(fā)翻譯異常,BadVAddr會(huì )保存現在處理的這個(gè)虛擬地址,EntryHi會(huì )填入這個(gè)虛擬地址的高位,還有Context中的內容會(huì )被重填。
然后就會(huì )考察C標志位,如果C=1,就會(huì )用緩沖作中轉,如果C=0,就不使用緩沖。
這幾級考察都通過(guò)了之后,就正確地找到了那個(gè)對應的物理地址。
c 如果沒(méi)有匹配的項,就會(huì )觸發(fā)一個(gè)TLB refill異常,然后后面就是軟件的工作了;

29、TLB的refill過(guò)程-軟件部分
a 計算這個(gè)虛擬地址是不是一個(gè)正確的虛擬地址,在內存頁(yè)表中有沒(méi)有與它對應的物理地址;如果沒(méi)有,則調用地址錯誤處理函數;
b 如果在內存頁(yè)表中找到了對應的物理地址,就將其載入寄存器;
c 如果TLB已經(jīng)滿(mǎn)了,就用random選取一個(gè)項丟棄;
d 復制新的項進(jìn)TLB。

30、MIPS Linux中標志內存頁(yè)已經(jīng)臟了的方式與x86不同。它要耍個(gè)把戲:
a 當一個(gè)可寫(xiě)的頁(yè)第一次載入內存中時(shí)(從磁盤(pán)載入?載入的時(shí)候就分配一個(gè)物理頁(yè),同時(shí)就分配個(gè)對應的虛擬頁(yè),并在內存頁(yè)表中添一個(gè)Entry),將其Entry的D標志位清0;
b 然后,當后面有指令要寫(xiě)這個(gè)頁(yè)時(shí),就會(huì )觸發(fā)一個(gè)異常(先載入TLB中判斷),我們在這個(gè)異常處理函數中把內存頁(yè)表項中的標志位D置1。這樣后面的就可以寫(xiě)了。并且,由于這個(gè)異常把標志位改了,我們認為這個(gè)物理頁(yè)是臟的了。
c 至于TLB中已經(jīng)有的那個(gè)Entry拷貝還要修改它的D標志位,這樣這次寫(xiě)入操作才能繼續入下進(jìn)行。

31、MIPS中的C語(yǔ)言參數傳遞機制?

32、MIPS中的堆棧結構及在內存中的分布?


指令長(cháng)度和寄存器個(gè)數
MIPS的所有指令都是32位的,指令格式簡(jiǎn)單。不像x86那樣,x86的指令長(cháng)度不是固定的,以80386為例,其指令長(cháng)度可從1字節(例如PUSH)到17字節,這樣的好處代碼密度高,所以MIPS的二進(jìn)制文件要比x86的大大約20%~30%。而定長(cháng)指令和格式簡(jiǎn)單的好處是易于譯碼和更符合流水線(xiàn)操作,由于指令中指定的寄存器位置是固定的,使得譯碼過(guò)程和讀指令的過(guò)程可以同時(shí)進(jìn)行,即固定字段譯碼。
32 個(gè)通用寄存器,寄存器數量跟編譯器的的要求有關(guān)。寄存器分配在編譯優(yōu)化中是最重要的優(yōu)化之一(也許是做重要的)?,F在的寄存器分配算法都是基于圖著(zhù)色的技術(shù)。其基本思想是構造一個(gè)圖,用以代表分配寄存器的各個(gè)方案,然后用此圖來(lái)分配寄存器。粗略說(shuō)來(lái)就是使用有限的顏色使圖中相臨的節點(diǎn)著(zhù)以不同的顏色,圖著(zhù)色問(wèn)題是個(gè)圖大小的指數函數,有些啟發(fā)式算法產(chǎn)生近乎線(xiàn)形時(shí)間運行的分配。全局分配中如果有16個(gè)通用寄存器用于整型變量,同時(shí)另有額外的寄存器用于浮點(diǎn)數,那么圖著(zhù)色會(huì )很好的工作。在寄存器數教少時(shí)候圖著(zhù)色并不能很好的工作。
   問(wèn): 既然不能少于16個(gè),那為什么不用64個(gè)呢?
答:使用64個(gè)或更多寄存器不但需要更大的指令空間來(lái)對寄存器編碼,還會(huì )增加上下文切換的負擔。除了那些很大不能感非常復雜的函數,32個(gè)寄存器就已足夠保存經(jīng)常使用的數據。使用更多的寄存器并不必要,同時(shí)計算機設計有個(gè)原則叫“越小越快”,但是也不是說(shuō)使用31個(gè)寄存器會(huì )比32個(gè)性能更好,32個(gè)通用寄存器是流行的做法。
指令格式
所有MIPS指令長(cháng)度相同,都是32位,但為了讓指令的格式剛好合適,于是設計者做了一個(gè)折衷:所有指令定長(cháng),但是不同的指令有不同的格式。MIPS指令有三種格式:R格式,I格式,J格式。每種格式都由若干字段(filed)組成,圖示如下:
I型指令
      6    5     5     16
   ------|-----|-----|------------------|
   | op | rs | rt   | 立即數操作 |
       ------|-----|-----|------------------|
加載/存儲字節,半字,字,雙字
條件分支,跳轉,跳轉并鏈接寄存器
R型指令
      6    5     5     5     5     6
   ------|-----|-----|-----|-----|--------|
   |op | rs   | rt   | rd |shamt|funct |
   ------|-----|-----|-----|-----|---------|
寄存器-寄存器ALU操作
讀寫(xiě)專(zhuān)用寄存器
J型指令
      6             26
   ------|------------------------------|
   |op   |  跳轉地址          |
       ------|------------------------------|
跳轉,跳轉并鏈接
陷阱和從異常中返回

各字段含義
op:指令基本操作,稱(chēng)為操作碼。
rs:第一個(gè)源操作數寄存器。
rt:第二個(gè)源操作數寄存器。
rd:存放操作結果的目的操作數。
shamt:位移量
funct:函數,這個(gè)字段選擇op操作的某個(gè)特定變體。
  
所有指令都按照著(zhù)三種類(lèi)型之一來(lái)編碼,通用字段在每種格式中的位置都是相同的。
    這種定長(cháng)和簡(jiǎn)單格式的指令編碼很規則,很容易看出其機器碼,例如:
add $t0,$s0,$s1
    表示$t0=$s0+$s1,即16號寄存器(s0)的內容和17號寄存器(s1)的內容相加,結果放到8號寄存器(t0)。
    指令各字段的十進(jìn)制表示為
   ------|-----|-----|-----|-----|------|
    0 | 16 | 17 |   8 |   0 |   32 |
   ------|-----|-----|-----|-----|------|
op=0和funct=32表示這是加法,16=$s0表示第一個(gè)源操作數(rs)在16號寄存器里,17=$s1表示第二個(gè)源操作數(rt)在17號寄存器里,8=$t0表示目的操作數(rd)在8號寄存器里。
把各字段寫(xiě)成二進(jìn)制,為
------|-----|-----|-----|-----|------|
   |000000|10000|10001|01000|00000|100000|
------|-----|-----|-----|-----|------|
這就是上述指令的機器碼(machine code),可以看出是很有規則性的。

通用寄存器(GPR)
有32個(gè)通用寄存器,$0到$31:
$0: 即$zero,該寄存器總是返回零,為0這個(gè)有用常數提供了一個(gè)簡(jiǎn)潔的編碼形式。MIPS編譯器使用slt,beq,bne等指令和由寄存器$0獲得的0 來(lái) 產(chǎn)生所有的比較條件:相等,不等,小于,小于等于,大于,大于等于。還可以用add指令創(chuàng )建move偽指令,即
move $t0,$t1
實(shí)際為
add $t0,$0,$t1
焦林前輩提到他移植fpc時(shí)move指令出錯,轉而使用add代替的。
   使用偽指令可以簡(jiǎn)化任務(wù),匯編程序提供了比硬件更豐富的指令集。
$1:即$at,該寄存器為匯編保留,剛才說(shuō)到使用偽指令可以簡(jiǎn)化任務(wù),但是代價(jià)就是要為匯編程序保留一個(gè)寄存器,就是$at。
由于I型指令的立即數字段只有16位,在加載大常數時(shí),編譯器或匯編程序需要把大常數拆開(kāi),然后重新組合到寄存器里。比如加載一個(gè)32位立即數需要 lui(裝入高位立即數)和addi兩條指令。像MIPS程序拆散和重裝大常數由匯編程序來(lái)完成,匯編程序必需一個(gè)臨時(shí)寄存器來(lái)重組大常數,這也是為匯編保留$at的原因之一。
$2..$3:($v0-$v1)用于子程序的非浮點(diǎn)結果或返回值,對于子程序如何傳遞參數及如何返回,MIPS范圍有一套約定,堆棧中少數幾個(gè)位置處的內容裝入CPU寄存器,其相應內存位置保留未做定義,當這兩個(gè)寄存器不夠存放返回值時(shí),編譯器通過(guò)內存來(lái)完成。
$4..$7:($a0-$a3)用來(lái)傳遞前四個(gè)參數給子程序,不夠的用堆棧。a0-a3和v0-v1以及ra一起來(lái)支持子程序/過(guò)程調用,分別用以傳遞參數,返回結果和存放返回地址。當需要使用更多的寄存器時(shí),就需要堆棧(stack)了,MIPS編譯器總是為參數在堆棧中留有空間以防有參數需要存儲。
$8..$15:($t0-$t7)臨時(shí)寄存器,子程序可以使用它們而不用保留。
$16..$23:($s0-$s7)保存寄存器,在過(guò)程調用過(guò)程中需要保留(被調用者保存和恢復,還包括$fp和$ra),MIPS提供了臨時(shí)寄存器和保存寄存器,這樣就減少了寄存器溢出(spilling,即將不常用的變量放到存儲器的過(guò)程),編譯器在編譯一個(gè)葉(leaf)過(guò)程(不調用其它過(guò)程的過(guò)程)的時(shí)候,總是在臨時(shí)寄存器分配完了才使用需要保存的寄存器。
$24..$25:($t8-$t9)同($t0-$t7)
$26..$27:($k0,$k1)為操作系統/異常處理保留,至少要預留一個(gè)。異常(或中斷)是一種不需要在程序中顯示調用的過(guò)程。MIPS有個(gè)叫異常程序計數器(exception program counter,EPC)的寄存器,屬于CP0寄存器,用于保存造成異常的那條指令的地址。查看控制寄存器的唯一方法是把它復制到通用寄存器里,指令mfc0(move from system control)可以將EPC中的地址復制到某個(gè)通用寄存器中,通過(guò)跳轉語(yǔ)句(jr),程序可以返回到造成異常的那條指令處繼續執行。仔細分析一下會(huì )發(fā)現個(gè)有意思的事情:
為了查看控制寄存器EPC的值并跳轉到造成異常的那條指令(使用jr),必須把EPC的值到某個(gè)通用寄存器里,這樣的話(huà),程序返回到中斷處時(shí)就無(wú)法將所有的寄存器恢復原值。如果先恢復所有的寄存器,那么從EPC復制過(guò)來(lái)的值就會(huì )丟失,jr就無(wú)法返回中斷處;如果我們只是恢復除有從EPC復制過(guò)來(lái)的返回地址外的寄存器,但這意味著(zhù)程序在異常情況后某個(gè)寄存器被無(wú)端改變了,這是不行的。為了擺脫這個(gè)兩難境地,MIPS程序員都必須保留兩個(gè)寄存器$k0和$k1,供操作系統使用。發(fā)生異常時(shí),這兩個(gè)寄存器的值不會(huì )被恢復,編譯器也不使用k0和k1,異常處理函數可以將返回地址放到這兩個(gè)中的任何一個(gè),然后使用jr跳轉到造成異常的指令處繼續執行。
$28:($gp)C語(yǔ)言中有兩種存儲類(lèi)型,自動(dòng)型和靜態(tài)型,自動(dòng)變量是一個(gè)過(guò)程中的局部變量。靜態(tài)變量是進(jìn)入和退出一個(gè)過(guò)程時(shí)都是存在的。為了簡(jiǎn)化靜態(tài)數據的訪(fǎng)問(wèn),MIPS軟件保留了一個(gè)寄存器:全局指針 gp(global pointer,$gp),如果沒(méi)有全局指針,從靜態(tài)數據去裝入數據需要兩條指令:一條有編譯器和連接器計算的32位地址常量中的有效位;令一條才真正裝入數據。全局指針只想靜態(tài)數據區中的運行時(shí)決定的地址,在存取位于gp值上下32KB范圍內的數據時(shí),只需要一條以gp為基指針的指令即可。在編譯時(shí),數據須在以gp為基指針的64KB范圍內。
$29:($sp)MIPS硬件并不直接支持堆棧,例如,它沒(méi)有x86的SS,SP,BP寄存器,MIPS雖然定義$29為棧指針,它還是通用寄存器,只是用于特殊目的而已,你可以把它用于別的目的,但為了使用別人的程序或讓別人使用你的程序,還是要遵守這個(gè)約定的,但這和硬件沒(méi)有關(guān)系。x86有單獨的PUSH和POP指令,而MIPS沒(méi)有,但這并不影響 MIPS使用堆棧。在發(fā)生過(guò)程調用時(shí),調用者把過(guò)程調用過(guò)后要用的寄存器壓入堆棧,被調用者把返回地址寄存器$ra和保留寄存器壓入堆棧。同時(shí)調整堆棧指針,當返回時(shí),從堆棧中恢復寄存器,同時(shí)調整堆棧指針。
$30:($fp)GNU MIPS C編譯器使用了偵指針(frame pointer),而SGI的C編譯器沒(méi)有使用,而把這個(gè)寄存器當作保存寄存器使用($s8),這節省了調用和返回開(kāi)銷(xiāo),但增加了代碼生成的復雜性。
$31:($ra)存放返回地址,MIPS 有個(gè)jal(jump-and-link,跳轉并鏈接)指令,在跳轉到某個(gè)地址時(shí),把下一條指令的地址放到$ra中。用于支持子程序,例如調用程序把參數放到$a0~$a3,然后jal X跳到X過(guò)程,被調過(guò)程完成后把結果放到$v0,$v1,然后使用jr $ra返回。
在調用時(shí)需要保存的寄存器為$a0~$a3,$s0~$s7,$gp,$sp,$fp,$ra。
跳轉范圍
J 指令的地址字段為26位,用于跳轉目標。指令在內存中以4字節對齊,最低兩個(gè)有效位不需要存儲。在MIPS中,每個(gè)地址的最低兩位指定了字的一個(gè)字節,cache映射的下標是不使用這兩位的,這樣能表示28位的字節編址,允許的地址空間為256M。PC是32位的,那其它4位從何而來(lái)呢?MIPS的跳轉指令只替換PC的低28位,而高4位保留原值。因此,加載和鏈接程序必須避免跨越256MB,在256M的段內,分支跳轉地址當作一個(gè)絕對地址,和 PC無(wú)關(guān),如果超過(guò)256M(段外跳轉)就要用跳轉寄存器指令了。
同樣,條件分支指令中的16位立即數如果不夠用,可以使用PC相對尋址,即用分支指令中的分支地址與(PC+4)的和做分支目標。由于一般的循環(huán)和if語(yǔ)句都小于2^16個(gè)字(2的16次方),這樣的方法是很理想的。

 

0 zero 永遠返回值為0
1 at 用做匯編器的暫時(shí)變量
2-3 v0, v1 子函數調用返回結果
4-7 a0-a3 子函數調用的參數
8-15 t0-t7 暫時(shí)變量,子函數使用時(shí)不需要保存與恢復
24-25 t8-t9
16-25 s0-s7 子函數寄存器變量。子函數必須保存和恢復使用過(guò)的變量在函數返回之前,從而調用函數知道這些寄存器的值沒(méi)有變化。
26,27 k0,k1 通常被中斷或異常處理程序使用作為保存一些系統參數
28 gp 全局指針。一些運行系統維護這個(gè)指針來(lái)更方便的存取“static“和”extern"變量。
29 sp 堆棧指針
30 s8/fp 第9個(gè)寄存器變量。子函數可以用來(lái)做楨指針
31 ra 子函數的返回地□

這些寄存器的用法都遵循一系列約定。這些約定與硬件確實(shí)無(wú)關(guān),但如果你想使用別人的代碼,編譯器和操作系統,你最好是遵循這些約定。

寄存器名約定與使用

*at: 這個(gè)寄存器被匯編的一些合成指令使用。如果你要顯示的使用這個(gè)寄存器(比如在異常處理程序中保存和恢復寄存器),有一個(gè)匯編directive可被用來(lái)禁止匯編器在directive之后再使用at寄存器(但是匯編的一些宏指令將因此不能再可用)。

*v0, v1: 用來(lái)存放一個(gè)子程序(函數)的非浮點(diǎn)運算的結果或返回值。如果這兩個(gè)寄存器不夠存放需要返回的值,編譯器將會(huì )通過(guò)內存來(lái)完成。詳細細節可見(jiàn)10.1節。


*a0-a3: 用來(lái)傳遞子函數調用時(shí)前4個(gè)非浮點(diǎn)參數。在有些情況下,這是不對的。請參考10.1細節。

* t0-t9: 依照約定,一個(gè)子函數可以不用保存并隨便的使用這些寄存器。在作表達式計算時(shí),這些寄存器是非常好的暫時(shí)變量。編譯器/程序員必須注意的是,當調用一個(gè)子函數時(shí),這些寄存器中的值有可能被子函數破壞掉。

*s0-s8: 依照約定,子函數必須保證當函數返回時(shí)這些寄存器的內容必須恢復到函數調用以前的值,或者在子函數里不用這些寄存器或把它們保存在堆棧上并在函數退出時(shí)恢復。這種約定使得這些寄存器非常適合作為寄存器變量或存放一些在函數調用期間必須保存原來(lái)值。

* k0, k1: 被OS的異?;蛑袛嗵幚沓绦蚴褂?。被使用后將不會(huì )恢復原來(lái)的值。因此它們很少在別的地方被使用。

* gp: 如果存在一個(gè)全局指針,它將指向運行時(shí)決定的,你的靜態(tài)數據(static data) 區域的一個(gè)位置。這意味著(zhù),利用gp作基指針,在gp指針32K左右的數據存取,系統只需要一條指令就可完成。如果沒(méi)有全局指針,存取一個(gè)靜態(tài)數據區域的值需要兩條指令:一條是獲取有編譯器和loader決定好的32位的地址常量。另外一條是對數據的真正存取。為了使用gp, 編譯器在編譯時(shí)刻必須知道一個(gè)數據是否在gp的64K范圍之內。通常這是不可能的,只能靠猜測。一般的做法是把small global data (小的全局數據)放在gp覆蓋的范圍內(比如一個(gè)變量是8字節或更小),并且讓linker報警如果小的全局數據仍然太大從而超過(guò)gp作為一個(gè)基指針所能存取的范圍。

并不是所有的編譯和運行系統支持gp的使用。

*sp: 堆棧指針的上下需要顯示的通過(guò)指令來(lái)實(shí)現。因此MIPS通常只在子函數進(jìn)入和退出的時(shí)刻才調整堆棧的指針。這通過(guò)被調用的子函數來(lái)實(shí)現。sp通常被調整到這個(gè)被調用的子函數需要的堆棧的最低的地方,從而編譯器可以通過(guò)相對於sp的偏移量來(lái)存取堆棧上的堆棧變量。詳細可參閱10.1節堆棧使用。

* fp: fp的另外的約定名是s8。如果子函數想要在運行時(shí)動(dòng)態(tài)擴展堆棧大小,fp作為楨指針可以被子函數用來(lái)記錄堆棧的情況。一些編程語(yǔ)言顯示的支持這一點(diǎn)。匯編編程員經(jīng)常會(huì )利用fp的這個(gè)用法。C語(yǔ)言的庫函數alloca()就是利用了fp來(lái)動(dòng)態(tài)調整堆棧的。

如果堆棧的底部在編譯時(shí)刻不能被決定,你就不能通過(guò)sp來(lái)存取堆棧變量,因此fp被初始化為一個(gè)相對與該函數堆棧的一個(gè)常量的位置。這種用法對其他函數是不可見(jiàn)的。

* ra: 當調用任何一個(gè)子函數時(shí),返回地址存放在ra寄存器中,因此通常一個(gè)子程序的最后一個(gè)指令是jr ra.

子函數如果還要調用其他的子函數,必須保存ra的值,通常通過(guò)堆棧。

對於浮點(diǎn)寄存器的用法,也有一個(gè)相應的標準的約定。在這里,我們已經(jīng)介紹了MIPS引入的寄存

指令實(shí)例:

1. load/store
  la $t0, val_1 復制val_1表示的地址到t0寄存器中     注: val_1是個(gè)Label
 lw $t2, ($t0) t0寄存器中的值作為地址,把這個(gè)地址起始的Word 復制到t2 中
 lw $t2, 4($t0) t0寄存器中的值作為地址, 把這個(gè)地址再加上偏移量4后 所起始的Word 復制到t2 中
 sw $t2, ($t0) 把t2寄存器中值(1 Word),存儲到t0的值所指向的RAM中
 sw $t2, -12($t0) 把t2寄存器中值(1 Word),存儲到t0的值再減去偏移量12, 所指向的RAM 中

2. 算數運算指令
  算數運算指令的所有操作數都是寄存器,不能直接使用RAM地址或間接尋址。
  操作數的大小都為 Word (4-Byte)
  指令格式與實(shí)例 注釋
  move $t5, $t1       // $t5 = $t1;
  add $t0, $t1,       // $t2 $t0 = $t1 + $t2; 帶符號數相加
  sub $t0, $t1,       // $t2 $t0 = $t1 - $t2; 帶符號數相減
  addi $t0, $t1, 5    // $t0 = $t1 + 5;
  addu $t0, $t1, $t2  // $t0 = $t1 + $t2; 無(wú)符號數相加
  subu $t0, $t1, $t2  // $t0 = $t1 - $t2; 無(wú)符號數相減
  mult $t3, $t4       // $t3 * $t4, 把64-Bits 的積,存儲到Lo,Hi中。即: (Hi, Lo) = $t3 * $t4;
  div $t5, $t6        // Lo = $t5 / $t6 (Lo為商的整數部分); Hi = $t5 mod $t6 (Hi為余數)
  mfhi $t0            // $t0 = Hi
  mflo $t1            // $t1 = Lo

3. 分支跳轉指令
 分支指令格式與實(shí)例 注釋
  b target 無(wú)條件的分支跳轉,將跳轉到target 標簽處
  beq $t0, $t1, target       // 如果 $t0 == $t1, 則跳轉到target 標簽處
  blt $t0, $t1, target       // 如果 $t0 < $t1,  則跳轉到target 標簽處
  ble $t0, $t1, target       // 如果 $t0 <=$t1,  則跳轉到target 標簽處
  bgt $t0, $t1, target       // 如果 $t0 > $t1,  則跳轉到target 標簽處
  bge $t0, $t1, target       // 如果 $t0 >= $t1, 則跳轉到target 標簽處
  bne $t0, $t1, target       // 如果 $t0 != $t1, 則跳轉到target 標簽處

4. 跳轉指令
 指令格式與實(shí)例 注釋
  j target          // 無(wú)條件的跳轉, 將跳轉到target 標簽處
  jr $t3            // 跳轉到t3寄存器所指向的地址處(Jump Register)

5. 子函數調用指令
 指令格式與實(shí)例 注釋
  jal sub_routine_label 執行步驟:
  a. 復制當前的PC(Program Counter)到$ra寄存器中。 因為當前的PC 值就是子函數執行完畢后的返回
       地址。
  b. 程序跳轉到子程序標簽sub_routine_label處?! ?br>  注:子函數的返回,使用 jr $ra  
  如果子函數內又調用了其他的子函數,那么$ra的值應該被保存到堆棧中。 因為$ra的值總是對應著(zhù)當前執
    行的子函數的返回地址。
本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
MIPS架構與匯編語(yǔ)言快速入門(mén)
MIPS構架簡(jiǎn)介 - MIPS技術(shù)及應用社區
x86_64架構下的函數調用及棧幀原理
[原創(chuàng )]X86內核筆記
討論MIPS 體系和CISC體系結構的不同之處
如何特意制造棧緩沖區溢出?(x86 & ARM)
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久