计算机语言中的基本单词成为指令,一台计算机的全部指令成为该计算机的指令集。
不同的指令集具有相似性,一方面是因为所有计算机都是基于基本原理相似的硬件技术所构建;另一方面所有计算机都必须提供一些基本操作;此外计算机的设计者有一个共同的目标:找到一种语言,可方便硬件和编译器的设计,且使性能最佳,同时成本和功耗最低。
MIPS操作数之32个寄存器
寄存器名字 | 寄存器编号 | 寄存器功能 |
---|---|---|
$zero |
$0 |
恒等于零 |
$at |
$1 |
被汇编器保留,用于处理大的常数 |
$v0 – $v1 |
$2-$3 |
存放函数返回值 |
$a0 – $a3 |
$4-$7 |
传递函数参数 |
$t0 – $t7 |
$8-$15 |
存放临时变量 |
$s0 – $s7 |
$16-$23 |
存放需要保存的临时值 |
$t8 – $t9 |
$24-$25 |
额外的存放临时变量 |
$k0 – $k1 |
$26-$27 |
用于操作系统内核 |
$gp |
$28 |
指向全局变量的指针 |
$sp |
$29 |
指向栈顶的指针 |
$fp |
$30 |
指向栈帧[^footnote2]的指针 |
$ra |
$31 |
返回地址,用于函数调用 |
MIPS汇编语言指令
MIPS汇编语言 | ||||
类别 | 指令 | 举例 | 含义 | 备注 |
算术运算 | 加 | add $s1, $s2, $s3 | $s1 = $s2 + $s3 | 三个操作数;检测溢出 |
减 | sub $s1, $s2, $s3 | $s1 = $s2 - $s3 | 三个操作数;检测溢出 | |
加立即数 | addi $s1, $s2, 100 | $s1 = $s2 + 100 | 加常数,检测溢出 | |
无符号加 | addu $s1, $s2, $s3 | $s1 = $s2 + $s3 | 三个操作数;不检测溢出 | |
无符号减 | subu $s1, $s2, $s3 | $s1 = $s2 - $s3 | 三个操作数;不检测溢出 | |
无符号加立即数 | addiu $s1, $s2, 100 | $s1 = $s2 + 100 | 加常数;不检测溢出 | |
从协处理器寄存器中获得 | mfc0 $s1, $epc | $s1 = $epc | 复制异常PC到专用寄存器 | |
乘 | mult $s2, $s3 | Hi, Lo = $s2 × $s3 | 64位有符号积存在Hi,Lo中 | |
无符号乘 | multu $s2, $s3 | Hi, Lo = $s2 × $s3 | 64位无符号积存在Hi,Lo中 | |
除 | div $s2, $s3 | Lo = $s2 / $s3 Hi = $s2 mod $s3 |
Lo = 商, Hi = 余 | |
无符号除 | divu $s2, $s3 | Lo = $s2 / $s3 Hi = $s2 mod $s3 |
无符号商和余数 | |
从Hi中获得 | mfhi $s1 | $s1 = Hi | 用来获得Hi的拷贝 | |
从Lo中获得 | mflo $s1 | $s1 = Lo | 用来获得Lo的拷贝 | |
数据传输 | 取字 | lw $s1, 20($s2) | $s1 = Memory[$s2 + 20] | 将一个字从内存取到寄存器中 |
存字 | sw $s1, 20($s2) | Memory[$s2 + 20] = $s1 | 将一个字从寄存器中存到内存中 | |
取无符号半字 | lhu $s1, 20($s2) | $s1 = Memory[$s2 + 20] | 将半个字从内存取到寄存器中 | |
存半字 | sh $s1, 20($s2) | Memory[$s2 + 20] = $s1 | 将半个字从寄存器中存到内存中 | |
取无符号字节 | lbu $s1, 20($s2) | $s1 = Memory[$s2 + 20] | 将一个字节从内存取到寄存器中 | |
存字节 | sb $s1, 20($s2) | Memory[$s2 + 20] = $s1 | 将一个字节从寄存器中存到内存中 | |
取链接字 | ll $s1, 20($s2) | $s1 = Memory[$s2 + 20] | 取字作为原子交换的前半部分 | |
存条件字 | sc $s2, 20($s2) | Memory[$s2 + 20] = $s1; $s1 = 0或1 |
存字作为原子交换的后半部分 | |
立即数读入高16位 | lui $s1, 100 | $s1 = 100 * 2^16 | 取立即数并放在高16位 | |
逻辑运算 | 与 | AND $s1, $s2, $s3 | $s1 = $s2 & $s3 | 三个寄存器操作数;按位与 |
或 | OR $s1, $s2, $s3 | $s1 = $s2 | $s3 | 三个寄存器操作数;按位或 | |
或非 | NOR $s1, $s2, $s3 | $s1 ~($s2 | $s3) | 三个寄存器操作数;按位或非 | |
与立即数 | ANDi $s1, $s2, 100 | $s1 = $s2 & 100 | 和常数按位与 | |
或立即数 | Ori $s1, $s2, 100 | $s1 = $s2 | 100 | 和常数按位或 | |
逻辑左移 | sll $s1, $s2, 10 | $s1 = $s2 << 10 | 根据常数左移相应位 | |
逻辑右移 | srl $s1, $s2, 10 | $s1 = $s2 >> 10 | 根据常数右移相应位 | |
条件跳转 | 相等时跳转 | beq $s1, $s2, 25 | if($s1 == $s2)跳至PC + 4 + 100 | 相等检测; 和PC相关的跳转 |
不相等时跳转 | bne $s1, $s2, 25 | if($s1 != $s2)跳至PC + 4 + 100 | 不相等检测; 和PC相关的跳转 | |
小于时置位 | slt $s1, $s2, $s3 | if($s2 < $s3) $s1 = 1;否则 $s1 = 0 | 比较是否小于;补码形式 | |
小于立即数时置位 | slti $s1, $s2, 100 | if($s2 < 100) $s1 = 1;否则 $s1 = 0 | 比较是否小于常数;补码形式 | |
无符号数比较小于时置位 | sltu $s1, $s2, $s3 | if($s2 < $s3) $s1 = 1;否则 $s1 = 0 | 比较是否小于;自然数 | |
比较小于立即数时置位 | sltiu $s1, $s2, 100 | if($s2 < 100) $s1 = 1;否则 $s1 = 0 | 比较是否小于常数;自然数 | |
无条件跳转 | 跳转 | j 2500 | 跳至10000 | 跳转至目标地址 |
跳转至寄存器所指的位置 | jr $ra | 跳至$ra | 用于switch语句,以及过程调用返回 | |
跳转并链接 | jal 2500 | $ra = PC + 4;跳至10000 | 用于过程调用 |
add a,b,c # the sum of b and c is placed in a
硬件设计基本原则:简单源于规整;越小越快;优秀的设计需要适宜的折中方案
原码:
反码:
补码:
指令在计算机内部是以若干或高或低的电信号的序列表示的,并且形式上和数的表示相同。
MIPS字段
1. R型(用于寄存器)
op(6位) |
rs(5位) |
rt(5位) |
rd(5位) |
shamt(5位) |
funct(6位) |
MIPS指令中各字段名称及含义如下:
2. I型(用于立即数)
op(6位) | rs(5位) | rt(5位) | constant or address(16位) |
虽然早期的计算机仅对整字进行操作,但人们很快发现,对字中的若干位组成的字段甚至于对某个位进行操作是很有用的。
第一类逻辑操作成为移位;第二类有用的操作是按位与、按位或;最后一类逻辑操作是是按位取反
计算机与简单计算器的区别在于决策能力
条件分支
beq register1,register2,L1 # if register1 equal register2, then go to L1
bne register1,register2,L1
案例分析
if(i == j)f = g + h;else f = g - h;
bne $s3,$s4,Else
add $s0,$s1,$s2 # f = g + h
j Exit
Else:sub $s0,$s1,$s2
Exit:
过程或函数是程序员进行结构化编程的工具,两者均有助于提高程序的可理解性和代码的可重用性。
在过程运行中,程序必须遵循一下6个步骤:
MIPS软件在位过程调用分配了32个寄存器时遵循以下约定:
调转和链接指令:跳转到某个地址的同时将下一条指令的地址保存到寄存器 $ra中的指令;
返回地址:指向调用点的链接,使过程可以返回合适的地址,在MIPS中存储在寄存器$ra中。
为了避免保存和回复一个其值未被使用过的寄存器,MIPS将18个寄存器分为两组:
案例分析
C:
int leaf(int g, int h, int i, int j)
{
int f;
f = (g+h)-(i+j);
return f;
}
MIPS:
leaf:
addi $sp,$sp,-12
sw $t1,8($sp);
sw $t0,4($sp);
sw $s0,0($sp);
add $t0,$a0,$a1 # g + h
add $t1,$a2,$a3 # i + j
sub $s0,$t0,$t1
add $v0,$s0,$zero
lw $s0,0($sp)
lw $t0,4($sp)
lw $t1,8($sp)
MIPS指令集中的读取立即数高位指令lui专门用来设置寄存器中常数的高16位,ori用来插入低16位。
当任务之间相互独立的时候,任务的并行执行是比较容易的。但往往任务之间需要相互协作,这种协作通常意味着某些任务写的结果是其他任务需要读取的值。这时执行读任务的一方要知道写任务什么时候完成了写操作,才能安全地读回数据。就是说,任务之间需要同步(synchronize),否则就有发生数据竞争(data race)的危险,导致读数据错误而引起程序运行结果的改变。
数据竞争:假如来自不同线程的两个访存请求访问同一个地址,它们连续出现,并且至少其中一个是写操作,那么这两个存储访问形成数据竞争。
编译器将C程序转换成一种机器能理解的符号形式的汇编语言程序。
汇编器将汇编语言指令转换成功能等价的机器语言。
链接器:也称链接编辑器。它是一个系统程序,把各个独立汇编的机器语言程序组合起来并且解决所有未定义的标记,最后生成可执行文件。
链接器的工作分3个步骤:
现在可执行文件已经在磁盘中,操作系统可以将其读入内存并启动执行它。在UNIX系统中,加载器(loader)按照如下步骤工作:
动态链接库:在程序执行过程中才被链接的库例程。