1.計算機中指令的表示
前言:
指令在計算機內部是用高低電平表示的,並且看上去和數的表示是一樣的。實際上,指令的各個部分都可以看成數,將這些數拼在一起就構成了指令。(實際上指令和數據的存儲確確實實是一樣的--都是二進制數)
在接下來的學習中需要用的部分的寄存器,所以在這里先做簡單引入。
寄存器$s0~$s7映射到寄存器16~23,寄存器$t0~$t7映射到寄存器8~15.(這里s和t都只是標號,在之后的學習中我們會知道,s代表保留寄存器,t代表臨時寄存器)
指令格式:
指令的布局形式叫做指令格式。
MIPS指令占32位,與數據字的位數相等。
數據字的定義:數據字,由於計算機使用的信息既有指令又有數據,所以計算機字可以代表指令,也可以代表數據。如果某字代表要處理的數據,則稱為 數據字;如果某字為一條指令,則稱為指令字-------百度百科
MIP字段:
op | rs | rt | rd | shamt | funct |
---|---|---|---|---|---|
6位 | 5位 | 5位 | 5位 | 5位 | 6位 |
操作碼 | 操作數寄存器1 | 操作數寄存器2 | 目的寄存器 | 位移量(之后介紹) | 功能碼 |
這是一種三地址指令,rd存放的是操作的結果。
問題:如果有些指令需要常數的參與,例如取數操作那該怎么做?
可能一開始想到的答案就是,把數放在rs,rt,rd段中,需要用常數的時候的時候,把他從去字段中取出。
但實際上,這種操作方法會出現問題,當參與操作的常數>32(2^5)時就會出現超出范圍的問題。因此又希望所有指令的長度一樣,又希望能夠有統一的指令格式,同時還要避免出現操作的常數太小的問題.......就出現了心得指令格式:
I型指令
前面所介紹的指令類型叫做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)指令用數的形式表達
2.邏輯操作
邏輯左移與邏輯右移
之前在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)和按位或(OR)
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相似,不再詳細展開。
按位取反(NOT)
該操作只有一個操作數,把這個操作數中的1->0,0->1
MIPS中還有或非NOR(NOT OR)
XOR
條件分支指令
相等條件分支
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部分,提高效率
case/switch
使用轉移地址表:由代碼中標簽所對應的地址構成的數組
程序跳轉-------->(索引)------>轉移地址表-------->(地址)-------->寄存器---------->(加載地址)-------->完成
為了支持這種情況,計算機提供了寄存器跳轉指令jr(jump regisiter),用來無條件跳轉到寄存器指定地址
(下下一節會詳細介紹jr語句)
4.循環
LOOP標記循環
例:用機器語言表示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???
小於則置位(set on less than)slt
若第一個寄存器小於第二個寄存器,則第三個寄存器置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