Home 指令系统
Post
Cancel

指令系统

CISC vs RISC

指令系统有两个的发展方向:

  • 增强原有指令的功能, 设置更为复杂的新指令实现软件功能的硬化. ——复杂指令系统计算机 CISC, 典型的如采用 x86 架构 (Intel 的) 的计算机.
  • 减少指令种类, 简化指令功能, 提高指令的执行速度. ——精简指令系统计算机 RISC, 典型的如采用 ARM、MIPS 架构的计算机.

直接跳转和间接跳转

正常执行的情况下, 指令会按照顺序一条条地执行, 使用跳转指令可以改变这种行为. 在汇编代码中, 这些跳转的目的地址通常用一个标号 label 指明. 在产生目标代码文件时, 汇编器会确定所有带标号的地址, 并将跳转目标 (目的指令的地址) 编码为跳转指令的一部分.

CSAPP 一书中截的图:

jmp.png

jmp 指令是无条件跳转, 它可以是直接跳转 (即跳转目标是作为指令的一部分编码的), 也可以是间接跳转 (即跳转目标是从寄存器或内存位置中读出的). 汇编语言中, 直接跳转是给出一个标号 (如 .L1) 作为跳转目标的. 间接跳转的写法是 * 后面跟一个寄存器或 (寄存器) 等. 例如, 指令 jmp *%eax 用寄存器 %eax 中的值作为跳转目标, 而 jmp *(%eax) 则以 %eax 中的值作为读地址, 然后从内存中读出跳转目标.

标志位的判断

数据存放在内存或 CPU 寄存器中都统一使用有符号数的补码.

ALU 中的运算无视有无符号, 统一使用一套逻辑, 只会根据信号进行逻辑运算.

  • 若为加法: [x]补 + [y]补
  • 若为减法: [x]补 - [y]补 = [x]补 + [y]求补

-[x]补 = +[-x]补 对补码表示的 y 求补, 得到的就是 -y. 如 5 表示为补码是 0101, 则 -5 是 1010+1=1011, 对 -5 的补码求补, 就是 0100+1=0101, 即 5. 注意这里是求补, 不是求补码. 求补不考虑符号位.

求一个数的补码对一个数求补 的区别: 补码是对于有符号数而言的.

求法: 对除符号位外的数据位全部取反后, 最低位+1.

如: -5 原码 = 1101 -> 除符号位取反: 1010 -> 最低位+1 -> 1011 = -5 的补码

求补则无视符号位, 这就对应了“ALU无视有无符号, 统一使用一套逻辑”的说法.

求法: 全部位数取反, 最低位+1.

如: -5 原码 = 1101 -> 全部位取反: 0010 -> 最低位+1 -> 0011

注: 正数补码=原码, 但无论正负都可以求补.

1.png

2.png

感谢这篇 博客 提供的参考 ~

溢出标志位 CF 和 OF

溢出是指运算结果超过了数的表示范围.

通常, 称大于机器所能表示的最大正数为上溢, 小于机器所能表示的最小负数为下溢.

而计算机是使用固定长度的机器数来表示数的, 其所能表示的范围是有限的. 运算结果超出了这个范围, 就会发生溢出.

溢出的产生只有两种情况:

仅当两个符号相同的数相加或两个符号相异的数相减时, 才会发生溢出.

  • 正数+正数 (或 正数-负数), 结果为负, 则产生了上溢;
  • 负数+负数 (或 负数-正数), 结果为正, 则产生了下溢.

补码定点数加减运算的溢出判断:

  • 采用一位符号位: 补码的减法运算在机器中是用加法器实现的, 因此补码加减运算最后都可以转化为 $[A]{补}+[B]{补}$. 也就是说, 无论是加法还是减法其实都是当作加法运算, 只要参与 + 的两个数符号相同, 结果又与原操作数符号不同, 则表示结果溢出.

    设 A 的符号位是 $A_S$, B 的符号位是 $B_S$, 运算结果 $A_S+B_S$ 的符号位是 $S_S$, 则 $V=A_SB_S\overline{S_S}+\overline{A_S}\overline{B_S}S_S$.

  • 采用双符号位: 双符号位法也称模 4 补码. 运算结果的两个符号位不同则表示溢出, 相同则表示未溢出. 此时最高位 $S_1$ 代表真正的符号位. $S_1S_2$ 中的 $S_2$ 实际上用来缓存溢出位.

    • 00表示结果为正, 无溢出;
    • 01表示结果正溢出;
    • 10表示结果负溢出;
    • 11表示结果为负, 无溢出.

    $V=S_1\equiv S_2$.

  • 采用一位符号位根据数据位的进位情况判断溢出: $C_S$: 符号位的最高进位

    $C_1$: 数据位的最高进位

    $V=C_S\equiv C_1$

CF

CF无符号数的进/借位标志, OF有符号数的溢出标志.

看到网上有些说法说无符号数不存在溢出, 只有进位. 其实要表达的意思很相像. CF 本来就叫进位/借位标志 (Carry Flag), 记录了运算结果的最高有效位向更高位的进位值, 或从更高位的借位值.

本来是没有办法保存最高位的进位值的 (因为超出了机器指定的长度), 但是可以把最高位的进位值保存在 CF 中. 对于 N 位数据来说, 最高位是第N-1位, 但是现在标志位相当于是假想的第N位.

例如: 8 位数据, 98H+98H, 产生进位, CF=1. 这个进位值在 8 位数据中无法保存, 但 CPU 并不会丢弃, 而是保存在标志位中. 标志位 CF 的计算方法:

  • 加法时: SUB = 0
  • 减法时: SUB = 1
  • CF = 最高位进位 ^ SUB

当两个数据相减时, 可能向更高位借位.

例如: 8 位数据, 97H-98H, 产生借位, 相当于计算 197H-98h, 于是 CF=0 (SUB=1, 最高位进位也是 1).

SUB 位的存在可以让我们知道刚才执行的是无符号数加法还是减法, 由此推断出是进位 (加法) 还是借位 (减法).

OF

例如: 对于 8 位有符号数据, 其数据范围是 -128~127. 计算 98+99 的值应该是 197, 但是因为数据溢出, 导致 98+99=-59 (本来如果作为无符号数是可以放下的, 但是作为有符号数就放不下, 这也是为什么需要将无符号数和有符号数的溢出区分开的原因)

因此 CPU 需要对指令执行之后是否溢出进行记录. 如果发生溢出, 则 OF=1; 如果没有发生溢出, 则OF=0.

总结:

  • 有符号运算: SF 记录正负, OF 记录溢出, CF 无意义;
  • 无符号运算: SF 无意义, OF 无意义, CF 记录进借位.
This post is licensed under CC BY 4.0 by the author.