《计算机组成与设计:硬件软件接口》第二章:2.6~2.10-阅读笔记

2.6分支指令

自动化计算机的实用性取决于循环使用一个给定指令序列的可能度,其循环次数依赖于计算结果。

--冯诺依曼

计算机和简单计算器不同的地方在于它能进行决策。根据不同的输入数据和中间计算结果,执行不同的指令。程序语言中,一般使用if语言表示分支,有时if语句和goto语句,标号(label)配合使用。MIPS汇编语言包括两条分支指令,它们类似带goto的if语句。第一条指令是

beq register1, register2, L1

这条指令的意思是:当Register1的值和Register2的值相等是,程序将分支执行标号为L1的语句。助记符beq表示如果相等则分支。第二条指令是:

bne register1, register2, L1

这条指令的意思是:当register1和register2的值不相等时,程序将分支执行标号位L1的语句。助记符bne表示如果不相等则分支。

通常,通过判断相反的条件来跳过if语句后面的then部分,代码的效率更高。

无条件分支指令:当遇到这条指令时,程序必须分支。

为了区分条件分支和无条件分支,MIPS将无条件分支指令命名为跳转(jump)指令简写为j

例如:j Exit #go to Exit

2.6.1循环

分支在if语句和循环程序中都起着重要作用:if语句中,用于二者选一;循环程序中用于重复计算。

Loop标签:我们需要添加Loop标签以便可以在循环的末端跳到开始的那条指令。

Loop: sll $t1, $s3, 2.

一个基本块是指这样一个指令序列:除了可能在序列结尾处,序列中没有分支指令;除可能在序列开始处,序列中没有分支目标和分支标号。编译的最初阶段就是将程序分成基本块。

slt   $t0, $s3, $s4

表示当寄存器$s3的值小于寄存器$s4的值时,寄存器$t0被设置为1;否则设置为0.

也有立即数版的:slti

2.6.2  Case/Switch语句

实现switch语句的一种方法是,借助一个条件判断序列,将switch语句转化为一系列if - then-else语句。

有时,另一种更有效的方法是通过编码形成一个分支指令序列地址表,即转移地址表;程序通过查找转移地址表以获取目标地址,并跳转到相应的分支指令序列。转移地址表是一个字数组,数组中的各个元素对应于代码中各个标号的地址。

为了支持上面这种情况,MIPS这类计算机包含一条寄存器跳转指令jr,实现无条件地转移到某个寄存器指定的地址。

2.7计算机硬件的过程支持

过程或函数是Chuo或java程序员进行结构化编程的一种工具,两者均有助于提高程序的可读性和代码的可重用性.过程允许程序员使用参数将过程与其余的程序和数据分离开,只允许传值和返回结果,从而每次将精力只集中在任务的一部分.在这节的结束部分我们描述了java的等价表示,但java对计算机的要求与C语言相同.

可以将过程想象成一个侦探,他离开时带着秘密计划,获得资源,执行任务,掩盖足迹,带着要求的结果返回初始点.一旦任务完成则应该不产生任何干扰.更重要的是,侦探是在"需要确切知道"的基础上操作,所以侦探不能对雇主有任何臆断.

同样,在过程运行期间,程序必须遵循以下六个步骤:

(1) 将参数存放在过程可以获取到的位置

(2) 向过程传递控制。

(3) 获得过程所需的存储资源

(4) 执行需要的任务

(5) 将结果的值放在调用程序可以获取到的地方

(6) 将控制返回到初始点,因为一个过程可能由一个程序中的几个点调用。

寄存器是计算机中保存数据最快的存储位置,所以我们希望尽量多地使用寄存器。MIPS软件在为过程调用分配32位寄存器时遵循以下规定:

        --$a0 ~ $a3: 四个参数寄存器,用于传递参数。

        --$v0 ~ $v1: 两个储值寄存器,用于返回值。

        --$ra: 一个返回地址寄存器,用于返回起始点。

除了分配寄存器,MIPS汇编语言包括一个过程指令:跳转到某个地址的同时将下一条指令的地址保存在寄存器$ra。这条跳转-链接指令简写为:

        jal  ProcedureAddress

指令名中的链接(link)部分表示指向调用位置的地址或链接,使过程可以返回到合适的地址。

链接存储在$ra寄存器中,称为返回地址。返回地址是必要的因为相同的过程可能在程序的不同部分调用。

 用一个寄存器保存当前运行的指令地址,这在存储程序概念中是绝对必要的。初遇历史原因,这个寄存器通常称为程序计数器(其更合理的名字是指令地址寄存器),在MIPS体系结构中简写为PC。jal指令将PC+4保存在$ra寄存器中,这样链接到下一条指令从而令过程可以返回。

寄存器跳转指令--jr, 用于非条件跳转到寄存器中指定的地址:jr  $ra

调用程序或称为调用者将参数值放在$a0~$a3中,使用jal X跳转到过程X(有时称为被调用者)。被调用者执行运算,将结果放在$v0~$v1,然后使用jr $ra 将控制返回到调用者。

2.7.1  使用更多寄存器

MIPS软件位栈分配了另一个寄存器:栈指针($sp),用于保存被调用者所需的寄存器。因为历史先例,栈“增长”是按照从高到低的地址顺序。这条规定意味着将值压栈时,栈指针减小;而值出栈时,栈长度缩短,栈指针增大。

MIPS将18个寄存器分为两组:
        --$t0 ~ $t9: 10个临时寄存器,在过程调用中被调用者(被调用的过程)不必保存。

        --$s0 ~ $s7: 8个保留寄存器,在过程调用中必须被保存

2.7.2嵌套过程

        没有调用其他过程的过程称为叶过程。

假如,假设主程序调用过程A,参数为3时,将值3存入存储器$a0然后使用jal A。再假设过程A通过jal B调用过程B,参数为7,同样存入$a0。因为A尚未结束任务,所以存在寄存器$a0使用上的冲突。同样在寄存器$ra存在返回地址的冲突,因为它现在保存着B的返回地址。

 一个解决办法是将其他所有必须保存的寄存器压栈。$sp(栈指针)本身的保存是通过被调用者将其被减去的值重新加上,其他寄存器则通过将它们保存到栈(如果被使用的话)再从栈中恢复它们来进行保存。这些动作也确保了调用者在退栈是能取到与压栈时存入的相同的值,因为被调用者在退栈时能取到与压栈时存入的相同的值,因为被调用者能够保证保护了$sp并且因为被调用者还保证不修改退栈中的调用者的入口地址,在这些过程调用时均保存在$sp以上的区域。

2.7.3  在栈中为新数据分配空间

栈中包含过程保存的寄存器和局部变量的段称为过程帧或活动记录。一些MIPS软件使用帧指针($fp)指向过程帧的第一个字。我们可以避免在过程中修改$sp来保护$fp。

2.7.4在堆中为新数据分配空间

堆位于静态数据段中之后的区域。注意这种分配允许堆和栈的相互增长,因此在两个段此消彼长的过程中达到内存的高效使用。C语言通过显式的函数调用在堆上分配和释放空间。malloc()在堆上分配空间并返回指向它的指针,free()释放指针指向的栈空间。

  2.8人机交互

ASCII码: 

可以使用一系列指令从一个字中析取出另一个字,所以字的读取和存储足以传送字节和字.然而,由于一些程序中文本的流行,所以MIPS仍然提供号字节转移指令.字节读取从内存中读出一个字节,放在寄存器的最右边8位.字节存储把寄存器最右边的8位取出来然后写到内存中.所以我们复制一个字节的顺序如下:
        lb $t0, 0($sp)   #read byte from source

        sb $t0, 0($gp)  #write byte to distination

字符通常连接为字符数目可变的字符串。有三种表示一个字符串的选择:

①字符串的第一个位置保留以给出字符串的长度

②附加带有字符串长度(如在结构体中)的变量

③字符串最后一个位置用一个字符来标识其结尾。

C使用第三种方法,用一个值为0的字节来结束字符串。所以字符串"cal"在C中用4字节表示。

 

 Java中的字符和字符串

        Unicode是大多数人类语言中字母的通用编码。Unicode中字母数和ASCII编码中有用的字符数一样多。为了更有包容性,Java为字符使用Unicode。它默认使用16位来表示一个字符。MIPS指令集具有外在的指令来读取和存储这样的16位半字。lh(load half)从存储器中读出一个半字,然后放在寄存器的最右边的16位。sh(store half)读出寄存器最右边的16位然后写进存储器。

字符串是带有专门的内置设置支持和用于连接、比较、转换的预定义方法的标准的Java类与C语言不同的是Java包括一个字来给出字符串长度,和Java数组相似。

2.9 对32位立即数的MIPS编址和寻址

        虽然保证所有的MIPS指令为32位长简化了硬件,但有时使用32位常量会32位地址会更方便。本节先介绍较大常量的一般解决方法,然后描述了在分支和跳转语句中指令寻址采用的优化。

2.9.1 32 位立即数

        虽然常量往往比较短而且适合16位字段,有时也比较打,MIPS指令集包括指令load upper immediat(取立即数高位)(lui)专门用于给寄存器中的常量设置高16位,允许后续指令设定常量的低16位。

lui指令将16位立即数字段的值存储到寄存器的高16位,用0填充低16位。

编译器或汇编程序必须把大的常数分开然后重组在一个寄存器中。像你想的那样,对内存地址饿得存取和立即数指令中的常数而言,立即数字段的大小限制确实存在问题。如果这个工作由汇编程序做,像MIPS软件一样,那么汇编程序必须有一个临时寄存器用于产生长整数值。这是给汇编程序保留$at寄存器的原因。

所以,MIPS机器语言的符号表示不再受硬件限制,而受汇编程序的构造者选择包括的内容所限。我们以靠近硬件层的方式来解释计算机的体系结构,注意当我们使用汇编程序的扩展语言时,那是在实际处理器中找不到的。

构造32位常数时要小心。addi指令将指令最左边的16位立即数字段复制到一个字的高16位中。2.5节的立即数逻辑或操作把0读到高16位中,所以被汇编程序用于和lui一起构造32位常数。

2.9.2分支和跳转中的寻址

MIPS跳转指令寻址最为简单。它们使用最后一种MIPS指令格式,叫做J型,包括6位操作码,其余的位为地址字段。如:j  10000。

和跳转指令不同,条件分支指令必须指定两个操作数和分支地址,如bne,beq等。如果程序地址必须适应16位域,那意味着没有大于2的16次方的程序,这在今天是一个很不现实的选择.一个可选的办法是指定一个总是加上分支地址的寄存器,所以一个分支指令可能如下计算:

        程序计数器 = 寄存器 + 分支地址.

这个和允许程序的大小达到2的32次方,仍能使用条件分支解决分支大小问题.然后问题是指定哪个寄存器呢?

答案取决于分支条件是怎样采用的.条件分支在循环和if语句中使用,它们倾向与转到一个附近的指令.

PC相对寻址:因为程序计数器(PC)包括了当前指令地址,我们可以转移到离当前指令距离位±2的十五次方个字的地方,如果我们使用PC来作为增加地址的寄存器。几乎所有循环和if语句都比2的十六次方小的多,所以PC是一个理想的选择。对硬件来说递增PC来指向下一条指令是很方便的。所以,MIPS寻址事实上是和下一条指令地址相关(PC+4),而不是当前指令PC。

MIPS体系结构通过对跳转和跳转-链接指令使用J-类型格式来为过程调用提供长地址。

因为MIPS指令都是4字节长,MIPS通过使用下一条指令地址为字数而不是字节数的PC相关寻址来延伸分支距离。所以,16位的字地址,相比16位的字节地址,跳转范围扩大了4倍。

2个字 = 8个字节 

2.9.3MIPS寻址模式总结

  多种不同寻址形式一般统称为寻址模式。MIPS寻址模式如下所示:

        (1) 寄存器寻址,操作数是寄存器。

        (2) 基址或偏移寻址,操作数在内存中,其地址是指令中基址寄存器和常数的和。

        (3)立即数寻址,操作数是指令中的常数

        (4)PC相对寻址,地址是PC计数器和指令中常数的和。

        (5)伪直接寻址,跳转地址是指令中26位和PC计数器的高位相连而成。

2.9.4机器语言解码

根据逆向工程将机器语言恢复到源汇编语言。

2.10程序的翻译和启动运行

2.10.1编译器

编译器将C程序转换成一种机器能理解的符号形式的汇编语言程序 

2.10.2汇编器

汇编器也能处理一些机器语言指令的常见变种,就像这些变种是它自己的指令一样。硬件不需要实现这些指令,;然而,它们在汇编语言中的存在简化了程序变换和编程、这类指令称为伪指令。总的来说,伪指令使MIPS拥有一个比已由硬件实现的更为丰富的汇编语言指令集。唯一的代价是保留了一个由汇编器使用的寄存器$at

 

2.10.3链接器

把所有独立汇编的机器语言程序拼接在一起

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值