汇编 - 标志位和跳转

跳转指令 大部分是基于 标志位 的值进行的,也有基于两个数的比较进行的。

标志位

进位标志C

进位/借位标志CF(Carry Flag):当运算结果的最高有效位出现进位或者借位的时候,进位/借位标志被设为1,否则为0。

我们考虑BB+6A=125,我们说它出现了进位,我们这么认为是因为我们假设了操作数的有效位为2,而计算结果的有效位为3,即两个最高只有十位的数相加得到了最高位数为百位的结果,按照这种理解49+6D=B6没有发生进位。相应的,10-9=7发生了借位,因为操作数最高位为十位,计算结果最高位位各位。但是,在OD中,每个数用4个字节(1字节=8bits)表示,取值范围从 00 00 00 00 ~ FF FF FF FF,操作数最高位为第8位,在这种情况下所谓的发生进位的BB+6A完整的写法是00 00 00 BB + 00 00 00 6A = 00 00 01 25,此时最高位(第8位)并未发生进位。另一个例子,FF FF FF FF + 00 00 00 01就会在第9位产生1,即发生了进位,这已经超过了寄存器允许的最大值,去除溢出的部分,计算结果为00 00 00 00借位类似,例如00 00 00 02 - 00 00 00 03 = FF FF FF FF

奇偶标志P

奇偶标志位PF(Parity Flag):当运算结果最低字节的二进制数中1的个数为偶数时PF=1,为奇数时PF=0。

为什么是最低字节?没太搞懂,在OD中:

  • ADD 8930, 0的结果显示P=1。8930H的二进制为1000 1001 0011 0000B,这个数中最低字节有两个1,总共五个1
  • ADD 8931, 0的结果显示P=0。尽管总共六个1,但是最低字节有3个1

零标志Z

零标志ZF(Zero Flag):当计算结果为0时Z=1,否为为0。
零标志常用来判断两个数是否相等。

符号标志S

符号标志SF(Sign Flag):我们通常说的有符号数指的是负数。当计算结果为负数时S=1,否则S=0。

四字节数的取值范围是 00 00 00 00 ~ FF FF FF FF,但是实际的取值范围还取决于该四字节数表示的有符号数还是无符号数

  • 如果是无符号数:四字节数的表面值就等于实际值;
  • 如果是有符号数:在有限的取值范围下,为了保证正数和负数数目相同,规定 00 00 00 00 ~ 7F FF FF FF 表示相当于十进制的 0 ~ 21,4748,3647,而 80 00 00 00 ~ FF FF FF FF 表示相当于十进制中的 -21,4748,3648 ~ -1。

溢出标志O

溢出标志O(Overflow Flag):只对有符号数的运算,在范围正中间附近(即7F FF FF FF ~ 80 00 00 00)时,计算结果的符号发生改变,则称之为溢出,此时O=1。计算结果的期望值与实际值不相等,是的计算结果发生错误。

例如 7F FF FF FF + 1 = 80 00 00 00 会形成溢出

换句话说,在OD中,如果计算结果的值超出了[-80000000H, 7FFFFFFFH]的范围就属于溢出

其它标志位: A T D

跳转指令

洒家总结了以下常见的跳转指令,大致可以分为4类:

  • 检查标志位进行跳转
  • 检查两个操作数进行跳转
  • 比较两个无符号数进行跳转
  • 比较两个有符号数进行跳转

基于标志位

常见的标志为 C(进位/借位),P(奇偶), Z(零), S(符号), O(溢出),标志位的取值有0和1两种。

标志位值为1时使用J前缀进行跳转,值为0时使用JN前缀进行跳转。

Description Flag J- 跳转1 JN- 跳转0
低字节中1的数目为偶数?Parity? -P JP JNP
计算结果为0?Zero? -Z JZ JNZ
有符号?是负数?Zero? -S JS JNS
溢出?Overflow? -O JO JNO
进位/借位?Carry? -C JC JNC

比较两个操作数

比较两个普通操作数

比较两个操作数使用CMP leftOp, rightOp,比较结果保存至标志位中,然后使用JE或者JNE进行跳转。

JEJNE根据标志位Z的结果判定是否需要跳转。JE和JZ、JNE和JNE实际上是等效的,都是以标志位Z为依据进行跳转。它们的不同在于直观理解上:

  • 后缀E(Equal)倾向于判断两个操作数是否相同
  • 后缀Z(Zero)倾向于检查标志位Z的值

在不同的场景中选择不同的语法是个很好的习惯。

检查寄存器值是否为0

检查寄存器的值是否为0,当然可以使用CMP,如CMP EAX, 0,但是更一般的,我们为寄存器设计了专用的指令。

指令结构为:J + [寄存器名称] + Z (表示Zero),例如:

  • JECXZ 表示当ECX=0时跳转
  • JCXZ 表示当CX=0时跳转

其它的寄存器检查类似。

比较两个无符号数

跳转前缀只使用 J,跳转后缀使用 B/E/A 系统 。

当第一个操作数 小于 第二个操作数时,记为 B=1,意为 Below。
当第一个操作数 等于 第二个操作数时,记为 E=1,意为 Equal;
当第一个操作数 大于 第二个操作数时,记为 A=1,意为 Above。
当然上述记法并不代表存在这样一个标志位B,E,A。

假设第一个操作数为left,第二个操作数为right,则

跳转条件 正面描述 指令 等价的反面描述 指令 (可以但没必要)
$\text{left} < \text{right}$ 小于 JB 不大于等于 JNAE
$\text{left} \le \text{right}$ 小于等于 JBE 不大于 JNA
$\text{left} = \text{right}$ 等于 JE 既不大于也不小于 JNAB (这个指令不存在)
$\text{left} > \text{right}$ 大于 JA 不小于等于 JNBE
$\text{left} \ge \text{right}$ 大于等于 JAE 不小于 JNB

比较两个有符号数

与无符号数比较类似,只是后缀采用 L/E/G 系统,分别代表 Less / Equal / Greater。

跳转条件 正面描述 指令 等价的反面描述 指令 (可以但没必要)
$\text{left} < \text{right}$ 小于 JL 不大于等于 JNGE
$\text{left} \le \text{right}$ 小于等于 JLE 不大于 JNG
$\text{left} = \text{right}$ 等于 JE 既不大于也不小于 JNGL (这个指令不存在)
$\text{left} > \text{right}$ 大于 JG 不小于等于 JNLE
$\text{left} \ge \text{right}$ 大于等于 JGE 不小于 JNL
>>>>>>>>>>>>> 转载请注明出处 <<<<<<<<<<<<<
0%