【用最少的指令来实现功能】
自用
文章基于《计算机体系结构新讲》(中国地质大学出版社)
目录
一、汇编指令
什么是汇编语言?
汇编语言(Assembly Language)是任何一种用于
电子计算机
、
微处理器
、
微控制器
或其他可编程器件的低级语言,亦称为符号语言。其中,本系列笔记将以MIPS汇编语言为例来进行解析。
(1)MIPS汇编指令示例解析
a=b+c //C风格
add a b c //MIPS汇编
指令代码 操作数1 操作数2 操作数3(MIPS汇编指令格式)
(2)MIPS指令集
二、汇编指令中的操作数
(1)寄存器
MIPS处理器的汇编语言中,算数运算指令的操作数只能是寄存器。(设计简单)
规定指令由四个部分构成(规则性、统一性)
在MIPS计算机中设计了32个32位的通用寄存器来作为汇编指令的操作数。
寄存器约定
*注:
$0:恒为常数0。
$s0~&s7:若使用,必须恢复。
$sp:若使用,必须恢复。
$ra:在嵌套调用时,调用函数需将它保存到栈上。
(2)立即数
类似于C语言里面的常数
为何叫立即数?
该数可以在代码中立即获得,不需要先存放到寄存器,等到用的时候再去取。
(3)内存
内存与寄存器的数据交换指令
将内存看作一个一维数组,从而可以使用一个指向内存的指针来简单访问内存
从内存中装入(load)数据到寄存器
lw $1 imm($2)s
# $1是用来存储从内存中获取的数据的寄存器
# $2是用来存储访问内存的指针,称为基址
# imm是偏移量(offset),必须是常数(有符号)
# $1=memory[$2 + offest]
将寄存器的值存储(store)到内存中
sw $1 imm($2)
# $1是用来保存即将要存入内存的数据的寄存器
# $2是用来存储访问内存的指针,称为基址
# imm是偏移量(offset),必须是常数(有符号)
# memory[$2 + offest]=$1
word 字 32位
1 word = 4 Byte = 32 Bit
上述memory[i]表示的是内存中的第i个字(word),而基址的单位是字节(Byte),offest的单位也是字节(Byte),但是不管是lw指令还是sw指令,每次但是传递一个字的内容,所以此时就要求基址和offest
的和为4的倍数(实现字对齐)。
以上全部是MIPS的字数据传送,下面将介绍MIPS中的字节数据传送:
字节传送指令:lb,sb
形如 lb $1 imm($2) 的指令,是将内存中第(imm+$2)字节的内容传递到$1寄存器中,但是第(imm+$2)字节的内容只有8位,但是寄存器有32位,此时MIPS就会采用符号拓展的方式(lbu指令是采用零拓展的方式)来填充高24位,从而与原有的8位数组成一个32位的数值保存到寄存器中。(sb指令就是将寄存器中一字节的数据传送到内存中)
三、MIPS程序控制指令
条件分支指令:
beq $1 $2 loop #若$1==$2,则跳转到loop,否则进行下一条指令
ben $1 $2 loop #若$1!=$2,则跳转到loop,否则进行下一条指令
无条件分支指令:
j loop #直接跳转到loop(类似与C语言中的goto)
结合条件分支指令实现C语言中if,while,for,switch等等到汇编语言的转换:
【if】
【C语言】
int a;
if(a==0)
clause 1;
else if(a<0)
clause 2;
else
clause 3;
【MIPS汇编】
假设$t0中存放的是a
beq $t0 $0 loop1 # $t0==$0 -> jump to loop1
slt $t1 $0 $t0 # $0<$t0 -> $t1=1 // $0>$t0 -> $t1=0
beq $t1 $0 loop2 # $t1==$0 -> jump to loop2
loop3:
clause 3
j exit
loop1:
clause 1
j exit
loop2:
clause 2
exit:
【while】
(1)do…while
【C语言】
do{
clause
}while(a>=100)
【MIPS汇编】
假设a存放在$t0中
li $t1 100 #int num=100 -> $t1
loop:
clause
slt $t2 $t0 $t1 # $t0<$t1 -> $t2=1 // $t0>=$t1 -> $t2=0
beq $t2 $0 loop # $t2==0 -> jump to loop
exit:
(2)while…
【C语言】
while(a>=100)
{
clause;
}
【MIPS汇编】
假设a存放在$t0中
li $t1 100 #int num=100 -> $t1
slt $t2 $t0 $t1 # $t0<$t1 -> $t2=1 // $t0>=$t1 -> $t2=0
li $t3 1 # $t3=1
beq $t2 $t3 exit # $t2==$t3 -> jump to exit
loop:
clause
slt $t2 $t0 $t1 # $t0<$t1 -> $t2=1 // $t0>=$t1 -> $t2=0
beq $t2 $0 loop # $t2==0 -> jump to loop
exit:
(3)课内上机do…while实例
题目:将下面C++代码翻译成对应的MIPS汇编语言
void main( )
{
int i, sum(0);
cin >> i;
do {
sum = sum + i * 2;
i++;
} while ( i<= 5);cout << “sum=” << sum << endl;
}
答案:
.data
I:.asciiz "please enter the value of I:"
result:.asciiz "sum="
.text
#print I
li $v0 4
la $a0 I
syscall
#Enter I
li $v0 5
syscall
#store the I to $to
move $t0 $v0
li $s0 0 #int sub=0
li $s1 5
Loop: add $t1 $t0 $t0
add $s0 $s0 $t1
addi $t0 $t0 1
slt $t2 $s1 $t0 #if $t0>5 $s1=1; if $t0<=5 $s1=0
beq $t2 $0 Loop
Exit:
#print result
li $v0 4
la $a0 result
syscall
#print sub
li $v0 1
move $a0 $s0
syscall
【for】
【C语言】
for(int i=0;i<100;i++)
{
clause;
}
【MIPS汇编】
li $t0 100 # $t0=100
add $t1 $0 $0 # int i=0 -> $t1
loop:
clause
addi $t1 $t1 1 # i++
slt $t2 $t1 $t0 # $t1<$t0 -> $t2=1 // $t1>=$t0 -> $t2=0
li $t3 1 # $t3=1
beq $t2 $3 loop # $t2==0 -> jump to loop
exit:
【switch】
【C语言】
switch(k)
{
case 0:
clause 0;
break;
case 1:
clause 1;
break;
case 2:
clause 2;
break;
case 3:
clause 3;
break;
default:
clause;
}
【MIPS汇编】
假设k存放在$t0中
bne $t0 $0 loop1 # k!=0 -> jump to loop1
loop0:
clause 0
j exit
loop1:
addi $t1 $t0 -1 # $t1=k-1
bne $t1 $0 loop2 # k!=1 -> jump to loop2
clause 1
j exit
loop2:
addi $t1 $t0 -2 # $t1=k-2
bne $t1 $0 loop3 # k!=2 -> jump to loop3
clause 2
j exit
loop3:
addi $t1 $t0 -3 # $t1=k-3
bne $t1 $0 loop4 # k!=3 -> jump to loop4
clause 3
j exit
loop4:
clause
exit:
四、函数调用
(一)跳转
课内上机实例
题目 :将下面C++语句翻译成对应的汇编语言
int Test(int a, int b)
{
int c = 0;
if (a >= b)
c = 4 * a + b;
else
c = 8 * a – b;
return c;
}void main()
{
int x = 5, y = 6;
cout << Test(x, y) << endl;
cout << Test(10, 20) << endl;
}
答案:
.data
first:.asciiz"Test(x, y) = "
second:.asciiz"\nTest(10, 20) = "
.text
main:
li $t1 5
li $t2 6
add $a1 $t1 $0
add $a2 $t2 $0
jal Test
#print first and result
li $v0 4
la $a0 first
syscall
li $v0 1
move $a0 $t0
syscall
li $t1 10
li $t2 20
add $a1 $t1 $0
add $a2 $t2 $0
jal Test
#print second and result
li $v0 4
la $a0 second
syscall
li $v0 1
move $a0 $t0
syscall
#finish
li $v0 10
syscall
Test:
add $t0 $t0 $0
slt $t3 $a2 $a1
beq $t3 $0 else
sll $t0 $t1 2
add $t0 $t0 $t2
else:
sll $t0 $t1 3
sub $t0 $t0 $t2
jr $ra
(二)嵌套
实例
【C语言】
main()
{
sumSquare(a,b);
}
int sumSquare(int x,int y)
{
return mult(x,x)+y;
}
int mult(int x,int y)
{
return x*y;
}
【MIPS汇编】
/* a,b : $s0,$s1 */
1000 add $a0 $s0 $0 #x=a
1004 add $a1 $s1 $0 #y=b
1008 addi $ra $0 1016 #$ra=1016
1012 j sumSquare
... #主函数
2000 sumSquare:
2004 add $t9 $ra $0 #$t9=1016
2008 addi $ra $0 2024 #$ra=2024
2012 add $t8 $a1 $a0 #$t8=$a1
2016 add $a1 $a0 $0 #$a1=$a0
2020 j mult
2024 add $a1 $t8 $0 #$a1=$t8=y(b)
2028 add $v0 $v0 $a1 #$v0= mult(x,y)+y
2032 add $ra $t9 $0 #$ra=$t9=1016
2036 jr $ra #jump to 1016
2040 mult:
2044 mul $v0 $a0 $a1 #$v0=x*y
2048 jr $ra #jump to 2024
(三)栈
对于更多层的嵌套,需要更多的寄存器,所以肯定会出现寄存器不够用的情况,同时在传参数的过程中,只有四个参数寄存器($a0~$a3,详见寄存器约定),也会出现不够用的情况,那么此时就要使用栈(stack)来保存这些数据。
栈
是一种先进后出的数据结构,在MIPS汇编语言中需要程序员自己维护栈。(自开自关)
实例:
前面(二)中实例中的sumSquare函数可以结合栈的使用编译成下面的汇编语言
sumSquare:
addi $sp $sp -8 #申请栈空间
sw $ra 4($sp) #保存主调函数的返回地址(即上面的1016)[$t9]
sw $a1 0($sp) #保存y[$t8]
add $a1 $a0 $0 #mult(x,x)将第二个参数复制成第一个
jal mult #跳转到mult,并且将下一条指令地址保存到$ra
lw $a1 0($sp) #恢复y
ad $v0 $v0 $a1 #mult()+y
lw $ra 4($ap) #取回主调函数的返回地址
addi $sp $sp 8 #恢复栈
jr $ra
mult:
*注:addi $sp $sp -n 中,n一定是4的倍数,且使用该栈是可以从0用到n-4(间隔为四)。
五、逻辑运算
常见的逻辑运算包括“与”运算和“或”运算,即and和or。
“与“运算(and)逻辑:当且仅当两个输入都为1时,结果才为1;否则为0。
”或“运算(or)逻辑:两个输入中只要有一个为1时,结果即为1;否则为0.
MIPS汇编语言中的三个移位指令:
(1)sll(逻辑左移):左移,空位补0。
(2)srl(逻辑右移):右移,空位补0.
(3)sra(算数右移):右移,空位进行符号拓展。
*注:左移可以用做乘2的幂次,右移可以用作除以2的幂次。