指令在計算機內部是用高低電平表示的,而且看上去和數的表示是同樣的。實際上,指令的各個部分均可以當作數,將這些數拼在一塊兒就構成了指令。(實際上指令和數據的存儲確確實實是同樣的--都是二進制數)數組
在接下來的學習中須要用的部分的寄存器,因此在這裏先作簡單引入。less
寄存器$s0~$s7映射到寄存器16~23,寄存器$t0~$t7映射到寄存器8~15.(這裏s和t都只是標號,在以後的學習中咱們會知道,s表明保留寄存器,t表明臨時寄存器)ide
指令的佈局形式叫作指令格式。佈局
MIPS指令佔32位,與數據字的位數相等。學習
數據字的定義:數據字,因爲計算機使用的信息既有指令又有數據,因此計算機字能夠表明指令,也能夠表明數據。若是某字表明要處理的數據,則稱爲 數據字;若是某字爲一條指令,則稱爲指令字-------百度百科測試
op | rs | rt | rd | shamt | funct |
---|---|---|---|---|---|
6位 | 5位 | 5位 | 5位 | 5位 | 6位 |
操做碼 | 操做數寄存器1 | 操做數寄存器2 | 目的寄存器 | 位移量(以後介紹) | 功能碼 |
這是一種三地址指令,rd存放的是操做的結果。spa
問題:若是有些指令須要常數的參與,例如取數操做那該怎麼作?設計
可能一開始想到的答案就是,把數放在rs,rt,rd段中,須要用常數的時候的時候,把他從去字段中取出。code
但實際上,這種操做方法會出現問題,當參與操做的常數>32(2^5)時就會出現超出範圍的問題。所以又但願全部指令的長度同樣,又但願可以有統一的指令格式,同時還要避免出現操做的常數過小的問題.......就出現了心得指令格式:blog
前面所介紹的指令類型叫作R型指令,即寄存器型指令,I型指令爲用於當即數的指令
op | rs | rt | constant or adress |
---|---|---|---|
6位 | 5位 | 5位 | 16位 |
操做碼 | 操做數寄存器1 | 操做數寄存器2 | 常數或者地址 |
例如:
1 lw $s0,32($s3) #取字指令 2 #$3存放在rs中,$0存放在rt中,32存放在address字段 3 #此時rt的意義已經發生了變化 4 #rt:指明接收取數結果的寄存器
四種操做方式所對應的指令格式:
指令 | 格式 | op | rs | rt | rd | shamt | funct | address |
---|---|---|---|---|---|---|---|---|
add | R | 0 | reg | reg | reg | 0 | 32(10) | n.a. |
sub | R | 0 | reg | reg | reg | 0 | 34(10) | n.a. |
addi(當即數) | I | 8(10) | reg | reg | n.a. | n.a. | n.a. | constant |
lw | I | 35(10) | reg | reg | n.a. | n.a. | n.a. | address |
(reg表明使用寄存器,address表明16位地址,n.a.表明不出現)(add和sub的op是相同的,區分他們的是funct)
經過觀察能夠發現,R型和I型的前三個字段長度相等,而且名稱也同樣;I型格式的第四個字段和R型後三個字段長度之和相等。R型和I型雖然功能不一樣可是卻構造很類似,而相關指令在二進制表示上的類似性能夠簡化硬件的設計。
1)指令用數的形式表達
以前在R型指令格式的介紹中,有一部分沒有介紹--shamt字段(shift amount)
shamt字段用來表示偏移量,常常用於邏輯左移(sll)和邏輯右移(srl)中。
邏輯左/右移:把一個字裏全部的位都向左/右移動,並在空出的地方補0
sll $t2,$s0,4 #reg $t2 = reg $s0 << 4 bits
op | rs | rt | rd | shamt | funct |
---|---|---|---|---|---|
0 | 0 | 16 | 10 | 4 | 0 |
邏輯左移還有額外的好處,左移i位= 數 * 2^i
AND提供了一種將源操做數置零的方法。
1 假設$t1 2 0000 0000 0000 0000 0000 1101 1100 0000 3 and $t0,$t1,$t2 #若是想把$t1第一字節置爲0,那麼就可讓$t2中相應位置爲0 4 t2 5 0000 0000 0000 0000 0000 1101 0000 0000 6 #$t2能夠叫作掩碼
與AND對偶的操做是按位或(OR)
OR提供了一種將源操做數置一的方法,與AND類似,再也不詳細展開。
該操做只有一個操做數,把這個操做數中的1->0,0->1
MIPS中還有或非NOR(NOT OR)
beg $s0,$s1,L1
若$s0和$s1中數值相等,則轉到標籤爲L1的語句
bne $s0,$s1,L1
若.......不相等,則轉到標籤爲L1的語句
當遇到這種指令時,程序必須分支
j L1
當遇到這條指令,則轉到標籤爲L1的語句
例:用機器語言表示C語言程序1
1 if( i == j ) 2 f = g + h; 3 else 4 f = g - h;
1 #$s3=i,$s4=j 2 #$s0=g,$s1=h,$s2=f 3 bne $s3,$s4,Else #若i!=j 則轉到Else 4 add $s0,$s1,$s2 #若i==j 則到這一步 f=g+h 5 j Exit #i==j段運行結束 6 Else: sub $s0,$s1,$s2 #i!=j f=g+h 7 Exit: #程序結束
選擇用bne而不是beg,由於bne經過測試分支的相反條件來體跳過if語句後邊的then部分,提升效率
使用轉移地址表:由代碼中標籤所對應的地址構成的數組
程序跳轉-------->(索引)------>轉移地址表-------->(地址)-------->寄存器---------->(加載地址)-------->完成
爲了支持這種狀況,計算機提供了寄存器跳轉指令jr(jump regisiter),用來無條件跳轉到寄存器指定地址
(下下一節會詳細介紹jr語句)
例:用機器語言表示C語言程序2
1 while( save[i] == k) 2 i=i+1;
1 $t3=i,$t5=k,$6=save 2 LOOP: sll $t1,$s3,2 #存放i的寄存器左移2位 3 add $t1,$t1,$t6 #把偏移地址與基址相加 4 lw $t0,0($t1) #取出save[i]中的數 5 bne $t0,$s5,Exit #save[i]!=k->Exit 6 addi $s3,$s3,1 #i++ 7 j LOOP #回到LOOP標記,從新運行 8 Exit:
i本來表明的是第i個數組元素,而按字節尋址是一次尋找一個字節
但爲何要把i*4???
若第一個寄存器小於第二個寄存器,則第三個寄存器置1,不然置0
1 slt $s0,$s3,$s4 #$s0 = 1 if $s3 < $s4 2 slti $t0,$s2,10 #$s0 = 1 if $s2 < 10
比較指令應該具備分清有符號數和無符號數的能力
有符號數操做(slt,slti):最高位爲1的數表明是一個複數,必定小於最高位爲0
無符號數操做(sltu,sltiu):最高位爲1,必定大於全部最高位爲0的數
補充:邊界檢查的捷徑
1 sltu $t0,$s1,$s2 #$t0=0 if $s1 >= length or $s1 < 0 2 beg $t0,$zero,IndoexOutofBounds