正文之前
今天折騰了一天的ubuntu windows雙系統(tǒng)祟辟,在windows下安裝ubuntu 16.04 然后 安裝引導(dǎo)啟動器的時候選擇的是/boot所在的sda12 每次除非開安全啟動氏仗,否則就沒法進(jìn)去系統(tǒng)墓怀,現(xiàn)在在 Try Ubuntu 下修復(fù)了一次之后 開機(jī)出現(xiàn)一個 grub>的命令行工具控妻,以前是直接開機(jī)一個 DHCP: \的東西赠法,好麻煩啊震束,而且按照教程 grub>下選擇了 (hd0,0)這種東西对雪,也沒法˙從C盤啟動啊河狐。心疼,現(xiàn)在試試try Ubuntu下的自動修復(fù)工具瑟捣,還不行我就放棄了馋艺。直接把原來的盤刪除,然后轉(zhuǎn)回ntfs格式算了迈套,虛擬機(jī)就虛擬機(jī)吧丈钙!
正文
1、過程或者函數(shù)是程序員進(jìn)行結(jié)構(gòu)化變成的工具交汤,在過程(子程序)運(yùn)行時雏赦,程序必須遵循以下6個步驟:
- 將參數(shù)放在過程可以訪問的位置劫笙,通俗來說就是,傳入的實(shí)參必須是函數(shù)能夠訪問的星岗;
- 把控制轉(zhuǎn)交給過程填大,跳轉(zhuǎn)到程序段·并且開始執(zhí)行;此過程一般是中斷
- 獲得過程所需要的資源俏橘,比如內(nèi)存允华,處理器等;
- 執(zhí)行需要的任務(wù)寥掐;
- 將結(jié)果的值放到調(diào)用程序可以訪問的位置靴寂;返回值咯
- 將控制返回初始點(diǎn),因?yàn)橐粋€過程可能由一個程序中的多個點(diǎn)調(diào)用召耘。
2百炬、結(jié)合上面的子程序的說法,我們知道在計(jì)算機(jī)中是寄存器的數(shù)據(jù)存取能力最快污它,所以我們希望盡量的多在寄存器中進(jìn)行任務(wù)剖踊,那么就有了如下幾個寄存器的分配
-
$a0~a3
四個寄存器用于傳遞參數(shù)進(jìn)去; -
$v0~v1
用于返回值的兩個寄存器衫贬; -
$ra
用于返回起始點(diǎn)的返回地址寄存器德澈。
3、除了寄存器外固惯,MIPS 還包括一條過程調(diào)用指令【
jal Index
】:跳轉(zhuǎn)到子程序的地址并且把當(dāng)前位置所在的下一條指令位置存入ra
寄存器中梆造,以用于子程序執(zhí)行完后的返回 .
為了支持這種情況,MIPS還使用了寄存器跳轉(zhuǎn)指令 jr 用于case語句葬毫,表示無條件跳轉(zhuǎn)到寄存器所指定的地址:jr $ra
镇辉。所以,綜上我們就有了如下的定義:調(diào)用程序稱為調(diào)用者caller供常,被調(diào)子程序稱為被調(diào)用者 callee摊聋。caller 把參數(shù)放入$ a0 ~ a3
然后使用jal X 調(diào)到callee位置鸡捐,然后callee執(zhí)行完畢后把返回值放到$ v0 ~ v1
然后再用jr $ra
跳回到caller的位置栈暇。jr
實(shí)際上就是PC+4這個位置保存到了ra
寄存器中。(PC是程序計(jì)數(shù)器的意思箍镜。不管是哪兒我感覺都是簡寫為PC源祈,就是說,這個東西指在當(dāng)前程序執(zhí)行的指令的位置色迂。)
4香缺、如果我們要使用更多寄存器?
不存在的歇僧。那就只能放到存儲器中了图张。然后在寄存器中的一個放入存儲器的地址锋拖,然后每次調(diào)用一波~。這個時候最理想的數(shù)據(jù)結(jié)構(gòu)的是棧祸轮。至于什么是棧兽埃,please call baidu ~ ,由于使用廣泛适袜,所以MIPS就把29號寄存器給了棧柄错,$ sp
就是棧指針,放入數(shù)據(jù)稱為壓棧苦酱,取出稱為出棧售貌。另外,棧指針是從高處往低處走疫萤,所以壓入數(shù)據(jù)會導(dǎo)致指針位置編號變小颂跨。
5、減少寄存器數(shù)據(jù)換出的約定
-
$t0 ~ t9
是十個臨時寄存器给僵,過程調(diào)用中不必被調(diào)用者(被調(diào)用的子程序)保存毫捣; -
$ s0 ~ s7
是八個保留寄存器,過程調(diào)用中必須被保存(一旦被使用帝际,就要由被調(diào)用者進(jìn)行保存和恢復(fù))蔓同。
6、上述內(nèi)容過一遍之后蹲诀,我們就可以揣測一個嵌套的過程斑粱,比如遞歸,自己調(diào)用自己這種用法脯爪!編譯一個遞歸C的過程则北,并且用MIPS表示出來:
我總結(jié)下,從第一個fact開始痕慢,每一個fact都會保存當(dāng)前的指令下一條的位置和當(dāng)前的n值到棧中尚揣,具體的表現(xiàn)就是addi $sp,$sp,-8;
棧的長度增加了2個“字”然后比較當(dāng)前的n值(保存在$a0
中)是否小于1,如果小于那么$t0
就等于1掖举,不小于就等于0快骗,然后把$t0
與0
進(jìn)行比較,如果等于就跳轉(zhuǎn)到L1塔次,不然就接著往下跑方篮,所以如果跳到L1,就會對$a0
內(nèi)保存的n值執(zhí)行-1的操作励负,然后繼續(xù)跳回到fact
進(jìn)行對n的檢查藕溅。但是如果不跳到L1,也就是說達(dá)到停止條件了继榆,那么就會忽略beq那一句往下跑巾表,開始從棧頂拋出數(shù)據(jù)汁掠,同時對返回把$v0
修改為$v0*$a0
,此處也就是fac(n)=(n-1)*n
集币;然后無條件跳轉(zhuǎn)到上一個調(diào)用程序fact的下一步调塌,直到結(jié)束!
int fact(int n)
{ if(n<1) return 1;
else return(n*fact(n-1));
}
//轉(zhuǎn)換為MIPS的代碼之后如下:
//以過程標(biāo)簽為開始惠猿,棧頂保存兩個寄存器羔砾,一個是返回地址,另一個是$a0
fact:
addi $sp,$sp,-8;
sw $ra,4($)sp;
sw $a0,0($sp)
//第一次調(diào)用的時候sw保存程序中調(diào)用fact的地址,然后指令測試n是否滿足結(jié)束條件小于1
slti $t0,$a0,1;
beq $t0,$zero,L1;
//如果n小于1偶妖,那么fact就把1置入一個寄存器及并且返回姜凄。
addi $v0,$zero,1;
addi $sp,$sp(8);
jr $ra;
//在從棧中退出兩個內(nèi)容之前,本應(yīng)該加載$a0 和$ra 但是由于n小于1的時候趾访,兩個寄存器的內(nèi)容不會有變化所以就跳過了上面的指令态秧,如果n不小于1,那么n-1后傳入fact中繼續(xù)調(diào)用fact:
L1:
addi $a0,$a0,-1;
jal fact;
//下一條指令時fact的返回位置扼鞋,從棧頂開始拋出數(shù)據(jù)(拋出數(shù)據(jù)的時候指針的位置序號會變大申鱼,前面說過的)
lw $a0,0($)sp;
lw $ra,4($sp);
addi $sp,8;
//接下來,返回值寄存器$v0得到原來的n-1的參數(shù)與當(dāng)前的返回內(nèi)的數(shù)據(jù)進(jìn)行相乘:
mul $v0,$a0,$v0;
//最后 fact 跳轉(zhuǎn)到返回地址云头;
jr $ra;
7捐友、MIPS匯編語言的寄存器約定,加速大概率事件的思想重要體現(xiàn)
8溃槐、J型跳轉(zhuǎn)指令匣砖,因?yàn)镸IPS中每一個操作指令的長度都是4字節(jié),所以其實(shí)可以用字來表示單位昏滴,也就是說猴鲫,立即數(shù)如果為20000,那么實(shí)際跳轉(zhuǎn)的長度就是80000byte谣殊,也就是20000個“字”拂共,這樣一來我們的跳轉(zhuǎn)返回就可以增加4倍,另外姻几,我們用“
PC
相對尋址”的時候嗎宜狐,其跳轉(zhuǎn)基準(zhǔn)是以PC+4
為基準(zhǔn)的,也就是說鲜棠,你跳轉(zhuǎn)的距離是相對于下一條指令來說的肌厨!這一點(diǎn)尤為重要培慌,后面會講到豁陆!另外,采用PC
跳轉(zhuǎn)尋址是一種加速大概率事件的思想的體現(xiàn)吵护!因?yàn)闂l件跳轉(zhuǎn)有一半都是跳轉(zhuǎn)到PC+4
的十六個指令范圍內(nèi)盒音。
正文之后
誰來救我1眵ⅰ!祥诽!Ubuntu雙系統(tǒng)要搞死我了F┦ァ!雄坪!引導(dǎo)修復(fù)要搞死我了厘熟。天哪。待我計(jì)算機(jī)組成原理大成之日维哈,就是我的dell電腦身死道消之時I獭!阔挠!在此立誓F!购撼!