【用最少的指令来实现功能】
自用
文章基于《计算机体系结构新讲》(中国地质大学出版社)
目录
一、汇编指令
(1)MIPS汇编指令示例解析
(2)MIPS指令集
二、汇编指令中的操作数
(1)寄存器
(2)立即数
(3)内存
三、MIPS程序控制指令
【if】
【while】
【for】
【switch】
四、函数调用
(一)跳转
(二)嵌套
(三)栈
五、逻辑运算
一、汇编指令
什么是汇编语言?
汇编语言(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]=$1word 字 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;
elseclause 3;【MIPS汇编】
假设$t0中存放的是abeq $t0 $0 loop1 # $t0==$0 -> jump to loop1slt $t1 $0 $t0 # $0<$t0 -> $t1=1 // $0>$t0 -> $t1=0beq $t1 $0 loop2 # $t1==$0 -> jump to loop2
loop3:clause 3j exit
loop1:clause 1j 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:clauseslt $t2 $t0 $t1 # $t0<$t1 -> $t2=1 // $t0>=$t1 -> $t2=0beq $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 -> $t1slt $t2 $t0 $t1 # $t0<$t1 -> $t2=1 // $t0>=$t1 -> $t2=0li $t3 1 # $t3=1beq $t2 $t3 exit # $t2==$t3 -> jump to exit
loop:clauseslt $t2 $t0 $t1 # $t0<$t1 -> $t2=1 // $t0>=$t1 -> $t2=0beq $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 Ili $v0 4la $a0 Isyscall#Enter Ili $v0 5syscall#store the I to $tomove $t0 $v0li $s0 0 #int sub=0li $s1 5Loop: add $t1 $t0 $t0add $s0 $s0 $t1addi $t0 $t0 1slt $t2 $s1 $t0 #if $t0>5 $s1=1; if $t0<=5 $s1=0beq $t2 $0 LoopExit:#print resultli $v0 4la $a0 resultsyscall#print subli $v0 1move $a0 $s0syscall
【for】
【C语言】
for(int i=0;i<100;i++)
{clause;
}【MIPS汇编】 li $t0 100 # $t0=100add $t1 $0 $0 # int i=0 -> $t1
loop:clauseaddi $t1 $t1 1 # i++slt $t2 $t1 $t0 # $t1<$t0 -> $t2=1 // $t1>=$t0 -> $t2=0li $t3 1 # $t3=1beq $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 0j exit
loop1:addi $t1 $t0 -1 # $t1=k-1bne $t1 $0 loop2 # k!=1 -> jump to loop2clause 1j exit
loop2:addi $t1 $t0 -2 # $t1=k-2bne $t1 $0 loop3 # k!=2 -> jump to loop3clause 2j exit
loop3:addi $t1 $t0 -3 # $t1=k-3bne $t1 $0 loop4 # k!=3 -> jump to loop4clause 3j 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;
}
答案:
.datafirst:.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 resultli $v0 4la $a0 first syscallli $v0 1move $a0 $t0syscallli $t1 10 li $t2 20 add $a1 $t1 $0 add $a2 $t2 $0 jal Test #print second and resultli $v0 4la $a0 second syscallli $v0 1move $a0 $t0syscall#finishli $v0 10syscall Test: add $t0 $t0 $0slt $t3 $a2 $a1 beq $t3 $0 else sll $t0 $t1 2add $t0 $t0 $t2
else:sll $t0 $t1 3sub $t0 $t0 $t2jr $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,并且将下一条指令地址保存到$ralw $a1 0($sp) #恢复yad $v0 $v0 $a1 #mult()+ylw $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的幂次。