思维导图
一.引言
指令是机器语言的基本组成单位,是一串二进制码.各种各样的指令使得机器进行不同的操作.由一个机器所有指令的集合,就叫做指令集.当前流行的指令集有ARM7,ARM8,MIPS,Intel x86,本文以MIPS指令集举例.
存储程序:我们将一系列的指令和数据以数字形式存放于存储器中,就成为了存储程序,以便于日后使用
寄存器:寄存器的主要功能就是用于数据的快速存取,刚才说到了存储程序,大量的数据其实是放在存储器中的,如何获取这些数据呢,就得使用寄存器了.机器可以利用寄存器中的数据执行指令,那么寄存器是不是越多越好呢?当然不是,使用寄存器的目的在于快速的存取数据,所以,过多的寄存器也不好,目前的寄存器的数量一般是32个.此外有一个寄存器的值$zero恒为0.
存储器:在寄存器中提到,存储器存储了大量的数据,此外,还可以储存数据结构,等等.
二.计算机硬件的操作数
- 字:由于寄存器的大小为32位,所以每次获取数据的大小不会超过32位,所以将每次获取的32位数据成为字,字就是获取数据单位.在下文中,用$t x 表示储存临时数据的寄存器,用$s x表示存储非临时数据的寄存器.
- 存储器操作数:为了同存储器获取或者存储数据,就会使用到数据传输指令,而数据传输指令会指定特定的地址,以获取或者储存数据.常用的数据传输指令是lw(load word)和sw(store word).
//数据传输指令
//以下两条指令完成了从存储器中的某个位置获取数据,然后又将此数据存入此位置的操作
//加载数据指令,其中$s0称为基址寄存器,用于储存基础地址,比如数组的起始地址,8称为偏移量,两者共同确认地址
lw $t0,8($s0)
//存储数据指令
sw t0,8($s0)
- 立即数操作数:如果没有立即数操作数,当我们希望将一个寄存器上的值加或减一个值的时候,我们需要使用数据传输指令从存储器中获取数据再使用算术指令,立即数操作数可以取代这个步骤,直接使用立即数操作数指令执行算术操作.
//如果使用立即数操作数,希望$s0的值+1
//首先获取值为1的地址,然后储存在临时寄存器中
lw $t0,32($s0)
//将$s1的值加一,意思是将$s1这个寄存器的值+1,然后储存在$s2中,第一个$s1是目标寄存器
add $s2,$s1,$t0
//如果使用立即数操作数的话
addi $s2,$s1,1
- 有符号数和无符号数:二进制数是一串0,1的排列,在MIPS中,最左边叫做最高有效位,最右边叫做最低有效位.因为计算机需要对正数和负数进行计算,所以需要一种方法来表示正数和负数.当不需要表示负数的时候,32位都是有效数字,称为无符号数.在指令命名中,一般使用u表示,比如,lbu,指加载无符号字节.
- 符号和幅值表示法:这是早先使用的方法,他会添加一个符号位用于表示正数和负数,但是添加在最左边还是最右边成了问题,还会发生+0和-0的问题,所以符号和幅值表示法逐渐被放弃.
- 二进制补码:使用前导位表示正负数,符号位(前导位)是0表示正数,前导位是1表示负数.符号位是有效值前的所有数字.如果一个数字没有32位,只有16,则需要进行符号扩展.具体操作是将此值的符号位最高有效位扩充16位.
//特别说明,前导位是指有效数字之前的所有数字
//这是1,前导位位0,说明是+1
0000 0000 0000 0000 0000 0000 0000 0001
//-1的表示方法,没有有效数字,所以有效数字是0,对负数求值需要取反,然后+1.
1111 1111 1111 1111 1111 1111 1111 1111
//这也是最大正数比负数少一的原因
//最大正数
0111 1111 1111 1111 1111 1111 1111 1111
//最大负数
1000 0000 0000 0000 0000 0000 0000 0000
//符号扩展
//初始值,1
0000 0000 0000 0001
//符号扩展为32位
0000 0000 0000 0000 0000 0000 0000 0001
三.计算机中指令的表示
首先,指令是有格式的,指令的长度也可以通过将二进制变为16进制或者其他进制来改变.
//二进制 1
0000 0000 0000 0000 0000 0000 0000 0001
//十六进制
0000 0001
3.1指令类型
以下是常用的指令
- 3.1.1算术指令
//加法,将寄存器s1,s2的值相加赋值到t0中
add $t0,$s1,$s2
//减法,将寄存器s1,s2的值相减赋值到t0中
sub $t0,$s1,$s2
//立即数加法,因为可以使用负数,所以没有立即数减法
//s1的是加上4赋值给t0
addi $t0,$s1,4
- 3.1.2数据传输指令
//加载数据指令
lw $t0,8($s0)
//存储数据指令
sw t0,8($s0)
//当然还要其他的数据传输指令比如lh $s1,20($2)是取半字指令,lb取字节,lbu取无符号字节ll取连接等等
- 3.1.3逻辑指令
逻辑指令分为移位和按位两种.
//移位指令,使得数据x2的冥或者/2的冥更加高效
//逻辑左移指令,将s1的左移4位储存在s0中,即将s1的值x2的4次方=16
sll $s0,$s1,4
//逻辑右移指令
srl $s0,$s1,4
------------------------------------------------------------------
//按位指令
//按位与,操作位均为1是结果为1,否则为0,有立即数指令
and $t0,$t1,$t2
addi $t0,$t1,1
//按位或,操作位任意为1结果为1,均为0则结果为0,有立即数指令
or $t0,$t1,$t2
ori $t0,$t1,1
//按位取反,操作位为1,结果为0,操作位为0,结果为1
not $t0,$t1
//按位或非,将后两个寄存器的值取或操作后再取反,赋值到$t0上
nor $t0,$t1,$t2
//按位异或,操作位不同结果为1,相同则结果为0
xor $t0,$t1,$t2
- 3.1.4决策指令
汇编语言中没有循环,switch这些语法,只有相等,不等,跳转至某个标签,并使用这些命令构造循环或者switch语句.
//判断指令,模拟以下java代码,其中a,b,c,d,e的值以储存在寄存器$s1,$s2,$s3,$s4,$s5中
if(a == b){
c = d + e;
}else{
c = d - e;
}
----------------------------------------------
//不等指令bne,branch if not equal,如果不等,则跳转到Else标签,此标签会在以后定义
bne $s1,$s2,Else
//如果相等,则执行add执行
add $s3,$s4,$s5
//跳转指令,跳转到指定标签Exit
j Exit
//Else标签定义
Else:sub $s3,$s4,$s5
//Exit标签定义
Exit:
----------------------------------------------
//同理,循环语句和switch语句也通过同样的方法构造
----------------------------------------------
//还有小于则置位指令,若s4小于s5,将s3设置为1,否则为0
slt $s3,$s4,$s5
//可以使用无符号数
sltu $s3,$s4,$s5
//也可以使用立即数
slti $s3,$s4,5
//或者两者结合
sltiu $s3,$s4,5
----------------------------------------------
//其他的跳转语句,跳转至指定寄存器位置,用于switch语句
jr $ra
//跳转并链接,用于过程调用
jal 200
3.2指令格式
现在说说为什么用$s x 和$t x表示寄存器了,在MIPS的指令格式中,$s0-$s7 用16-23表示,$t0-$t7用8-15表示
指令格式有三种
- R型
OP | RS | RT | RD | SHAMT | FUNCT |
6位 | 5位 | 5位 | 5位 | 5位 | 6位 |
- op: 操作码,确定使用哪个指令
- rs:第一个源操作数寄存器
- rt:第二个源操作数寄存器
- rd:用于存放操作结果的目的寄存器,举例:add $t0,$s1,$s2,$t0是rd,$t0,$s1是rs和rt
- shamt:表示位移指令中的位移量比如sll $t0,$s1,4 的shamt就是4
- funct:功能码,表示特定op字段的特定变式
- I型
OP | RS | RT | 常量值或者地址 |
6位 | 5位 | 5位 | 16位 |
当使用取字指令时,如果偏移量相当的大,大于32(5bit的最大值 0001 1111),R型指令就不能完成此功能,所以I型指令将RT后的字段全部设置为一个字段用于表示常量值或者地址,其他字段的功能则没有发生变化.此时偏移量或者地址的最大值就是2^15
- J型
OP | ADRESS |
6位 | 26位 |
J型指令则是专门用于跳转指令,指定相当大的偏移量.
以下是常用指令指令及其格式
四.MIPS 指令集
MIPS 指令集(共31条) | |||||||||
助记符 | 指令格式 | 示例 | 示例含义 | 操作及其解释 | |||||
Bit # | 31..26 | 25..21 | 20..16 | 15..11 | 10..6 | 5..0 |
|
|
|
R-type | op | rs | rt | rd | shamt | func |
|
|
|
add | 000000 | rs | rt | rd | 00000 | 100000 | add $1,$2,$3 | $1=$2+$3 | rd <- rs + rt ;其中rs=$2,rt=$3, rd=$1 |
addu | 000000 | rs | rt | rd | 00000 | 100001 | addu $1,$2,$3 | $1=$2+$3 | rd <- rs + rt ;其中rs=$2,rt=$3, rd=$1,无符号数 |
sub | 000000 | rs | rt | rd | 00000 | 100010 | sub $1,$2,$3 | $1=$2-$3 | rd <- rs - rt ;其中rs=$2,rt=$3, rd=$1 |
subu | 000000 | rs | rt | rd | 00000 | 100011 | subu $1,$2,$3 | $1=$2-$3 | rd <- rs - rt ;其中rs=$2,rt=$3, rd=$1,无符号数 |
and | 000000 | rs | rt | rd | 00000 | 100100 | and $1,$2,$3 | $1=$2 & $3 | rd <- rs & rt ;其中rs=$2,rt=$3, rd=$1 |
or | 000000 | rs | rt | rd | 00000 | 100101 | or $1,$2,$3 | $1=$2 | $3 | rd <- rs | rt ;其中rs=$2,rt=$3, rd=$1 |
xor | 000000 | rs | rt | rd | 00000 | 100110 | xor $1,$2,$3 | $1=$2 ^ $3 | rd <- rs xor rt ;其中rs=$2,rt=$3, rd=$1(异或) |
nor | 000000 | rs | rt | rd | 00000 | 100111 | nor $1,$2,$3 | $1=~($2 | $3) | rd <- not(rs | rt) ;其中rs=$2,rt=$3, rd=$1(或非) |
slt | 000000 | rs | rt | rd | 00000 | 101010 | slt $1,$2,$3 | if($2<$3) | if (rs < rt) rd=1 else rd=0 ;其中rs=$2,rt=$3, rd=$1 |
sltu | 000000 | rs | rt | rd | 00000 | 101011 | sltu $1,$2,$3 | if($2<$3) | if (rs < rt) rd=1 else rd=0 ;其中rs=$2,rt=$3, rd=$1 |
sll | 000000 | 00000 | rt | rd | shamt | 000000 | sll $1,$2,10 | $1=$2<<10 | rd <- rt << shamt ;shamt存放移位的位数, |
srl | 000000 | 00000 | rt | rd | shamt | 000010 | srl $1,$2,10 | $1=$2>>10 | rd <- rt >> shamt ;(logical) ,其中rt=$2, rd=$1 |
sra | 000000 | 00000 | rt | rd | shamt | 000011 | sra $1,$2,10 | $1=$2>>10 | rd <- rt >> shamt ;(arithmetic) 注意符号位保留 |
sllv | 000000 | rs | rt | rd | 00000 | 000100 | sllv $1,$2,$3 | $1=$2<<$3 | rd <- rt << rs ;其中rs=$3,rt=$2, rd=$1 |
srlv | 000000 | rs | rt | rd | 00000 | 000110 | srlv $1,$2,$3 | $1=$2>>$3 | rd <- rt >> rs ;(logical)其中rs=$3,rt=$2, rd=$1 |
srav | 000000 | rs | rt | rd | 00000 | 000111 | srav $1,$2,$3 | $1=$2>>$3 | rd <- rt >> rs ;(arithmetic) 注意符号位保留 |
jr | 000000 | rs | 00000 | 00000 | 00000 | 001000 | jr $31 | goto $31 | PC <- rs |
I-type | op | rs | rt | immediate |
|
|
| ||
addi | 001000 | rs | rt | immediate | addi $1,$2,100 | $1=$2+100 | rt <- rs + (sign-extend)immediate ;其中rt=$1,rs=$2 | ||
addiu | 001001 | rs | rt | immediate | addiu $1,$2,100 | $1=$2+100 | rt <- rs + (zero-extend)immediate ;其中rt=$1,rs=$2 | ||
andi | 001100 | rs | rt | immediate | andi $1,$2,10 | $1=$2 & 10 | rt <- rs & (zero-extend)immediate ;其中rt=$1,rs=$2 | ||
ori | 001101 | rs | rt | immediate | andi $1,$2,10 | $1=$2 | 10 | rt <- rs | (zero-extend)immediate ;其中rt=$1,rs=$2 | ||
xori | 001110 | rs | rt | immediate | andi $1,$2,10 | $1=$2 ^ 10 | rt <- rs xor (zero-extend)immediate ;其中rt=$1,rs=$2 | ||
lui | 001111 | 00000 | rt | immediate | lui $1,100 | $1=100*65536 | rt <- immediate*65536 ;将16位立即数放到目标寄存器高16 | ||
lw | 100011 | rs | rt | immediate | lw $1,10($2) | $1=memory[$2 | rt <- memory[rs + (sign-extend)immediate] ;rt=$1,rs=$2 | ||
sw | 101011 | rs | rt | immediate | sw $1,10($2) | memory[$2+10] | memory[rs + (sign-extend)immediate] <- rt ;rt=$1,rs=$2 | ||
beq | 000100 | rs | rt | immediate | beq $1,$2,10 | if($1==$2) | if (rs == rt) PC <- PC+4 + (sign-extend)immediate<<2 | ||
bne | 000101 | rs | rt | immediate | bne $1,$2,10 | if($1!=$2) | if (rs != rt) PC <- PC+4 + (sign-extend)immediate<<2 | ||
slti | 001010 | rs | rt | immediate | slti $1,$2,10 | if($2<10) | if (rs <(sign-extend)immediate) rt=1 else rt=0 ; | ||
sltiu | 001011 | rs | rt | immediate | sltiu $1,$2,10 | if($2<10) | if (rs <(zero-extend)immediate) rt=1 else rt=0 ; | ||
J-type | op | address |
|
|
| ||||
j | 000010 | address | j 10000 | goto 10000 | PC <- (PC+4)[31..28],address,0,0 ;address=10000/4 | ||||
jal | 000011 | address | jal 10000 | $31<-PC+4; | $31<-PC+4;PC <- (PC+4)[31..28],address,0,0 |
注:本篇文章由《计算机组成与设计》第二章-指令:计算机的语言 总结而来,由于本人非计算机专业出身,许多知识实在是理解不能,总结有相当多的遗漏,乃是我看不懂所致,更别说其中内容肯定有大量的理解错误,万望大家提出批评,我好改正。