当前位置: 首页 > 知识库问答 >
问题:

如何计算x86汇编中的辅助标志状态

东方震博
2023-03-14

如何在x86汇编中计算辅助标志?

我能找到的大多数资源都解释说,如果存在从第3位到第4位的进位,则辅助标志设置为“1”。

Wiki:

它指示在执行算术指令后从累加器寄存器的最低有效四位生成进位或借位的时间。

示例:

mov al,-14 *(1111 0010)
mov bl,-130 (0111 1110)
sub al,bl (1111 0010 – 0111 1110)

*括号显示存储的二进制模式

结果:<代码>1111 0010–0111 1110将使用2的补码计算为<代码>1111 0010 1000 0010,得出的结果为<代码>0111 0100。

在给定的示例中,AF设置为(=1)。我不明白为什么会这样,因为我看不到从第3位到第4位有进位。添加00100010,最低有效的半字节,等于0100,无进位。累加器寄存器的最低有效四位已从0010更改为0100('2'to'4'),从低半字节到高半字节没有进位?

有人能好心地解释一下我的想法哪里出错了吗?

我怀疑大量的“否定”在某种程度上会让我感到厌烦,因为我在调试器中尝试了几个不同的示例,它们都按照我的期望行事,除了这一个示例之外。

共有1个答案

金子平
2023-03-14

x86 CPU上的sub指令是自第一个芯片8086以来的“真实”指令,即它不是某种汇编程序的便利,它被翻译为否定加法,但它有自己的二进制操作码,CPU本身会意识到它应该产生减法的结果。

该指令具有Intel的定义,它如何影响标志,并且在本例中,标志被修改为“仿佛”计算实数减法。当您专注于编程算法或检查某些代码的正确性时,您只需要知道这些。芯片本身是否将其实现为加法,是否有一些额外的晶体管将标志转换为“减法”变体,这是“实现细节”,只要您只想知道结果,这并不重要。

当您调整特定代码的性能时,实现细节变得很重要,然后考虑芯片的内部架构和特定操作码的实现可能会给您一些想法,如何以更不直观/非人工的方式重写特定代码,通常甚至使用比“原始”版本更多的指令,但性能会更好,由于更好地利用了芯片的内部实现。

但是结果定义得很好,并且不能通过一些实现细节来更改,这将是“CPU中的错误”,就像第一批奔腾芯片为某些分区计算错误的结果一样。

也就是说,汇编指令的定义已经像其他语言一样泄露了实现细节,因为汇编指令在设计时是“在HW晶体管中创建简单的东西”和“使一些编程有意义的东西”的一半,而其他更高级别的编程语言更偏向于“有意义的东西”,只是不情愿地从HW实现中强加了一些繁琐的限制,例如变量类型的特定位大小的值范围。

因此,对实现的好奇以及为什么某些事情被定义为它们的本来面目(例如,为什么12月30日不更新CF标志,否则它只是子xxx,1)通常会让你对某些任务如何在汇编中更有效地编写、芯片如何发展以及哪些任务比其他任务更容易计算有新的见解。

但首先是基础知识。sub指令更新标志,就好像计算了减法一样,sub指令不知道它正在处理的值的任何上下文,它得到的只是值的二进制模式,在您的情况下:1111_0010-0111_1110,当在有符号8位数学“-14-126”中解释时(-130不适合8位,因此它被截断为126,好的汇编器会在那里发出警告/错误),或者在无符号8b数学“242-126”中解释时。在有符号数学的情况下,结果应该是-140,它被截断(溢出发生,OF=1)8b值116,在无符号数学的情况下,结果是116,没有无符号溢出(携带/借用CF=0)。

减法本身是按位定义的,即。

         1111_0010
       – 0111_1110
       ___________
result:  0111_0100
borrow:  0111_1100
              ^ this borrow goes to AF
         ^ the last borrow goes to CF
         ^ the last result bit goes to SF
  All zero result bits sets ZF=1
  PF is calculated from only low 8 bits of result (even with 32b registers!)
  where PF=1 means there was even number of set bits, like here 4.

你可以从右向左,做每位减法,即0-0=0, 1-1=0, 0-1=1b,0-2=0 b等(其中b表示需要“借用”,即第一个操作数借用了2(下一位为1),使结果有效位值为0或1)

顺便说一句,在位级别上的OF集的精确程度有点棘手,这里有一些很好的Q a,因此,您可以搜索它,但从数学角度来看,如果结果在有符号解释中被“截断”(如本例中),则OF被设置。这就是它的定义方式(实现也遵循这一点)。

如您所见,所有标志都按定义设置,sub甚至不知道,如果第一个参数是-14242,因为这不会改变位级别上的任何东西,指令将只是从另一个中减去一个位模式并按定义设置所有标志,完成。位模式确实代表了什么,以及如何解释标志结果,这取决于以下指令(代码逻辑),但与sub本身无关。

减法仍然有可能在CPU内通过加法实现(虽然不太可能,但实现减法并不困难),并通过一些额外的标志处理来修复标志,但这取决于特定的芯片实现。

请注意,现代x86是一个相当复杂的野兽,它首先将经典x86指令转换为微代码操作,在可能的情况下对它们重新排序以避免暂停(如等待内存芯片的值),有时并行执行多个微操作(一次最多执行3个IIRC操作),并且使用100个动态重命名/映射为原始的物理寄存器(如代码中的al、bl),也就是说,如果您将asm的这3行复制两次,现代x86 CPU实际上可能会与两个不同的物理“al”寄存器并行执行,然后下一个请求“al”结果的代码将从后一个寄存器获得该值,第一个明显被第二个子系统丢弃。但所有这些都被定义为创建可观察的结果,“就像经典的8086在实际的单个物理AL寄存器上按顺序分别运行每条指令一样”,至少在单核意义上(在多核/线程设置中,有额外的指令允许程序员在特定的代码点序列化/最终确定结果,然后其他核/线程可以检查它们以一致的方式查看它们)。

因此,只要您只是学习x86汇编基础知识,您甚至不需要知道,现代x86 CPU中有一些微体系结构,可以将您的机器代码转换为不同的机器代码(程序员无法直接使用这些代码,因此没有“现代x86微汇编”,您可以直接编写这些微操作,您只能生成常规的x86机器代码,并让CPU自己处理内部实现。

 类似资料:
  • 我正在使用Visual Studio 2013 Ultimate在MASM中编程汇编语言(x86)。我试图使用数组来计算n个元素的斐波那契序列。换句话说,我正在尝试到一个数组元素,获取它前面的两个元素,将它们相加,并将结果存储在另一个数组中。 我在建立索引寄存器时遇到了麻烦。 我的程序设置是这样的: 我无法编译该代码,因为有一条错误消息显示行的“error a2031:must be index

  • 本文向大家介绍Intel x86 Assembly& Microarchitecture x86汇编语言,包括了Intel x86 Assembly& Microarchitecture x86汇编语言的使用技巧和注意事项,需要的朋友参考一下 示例 x86汇编语言家族代表了最初的Intel 8086架构数十年来的进步。除了基于所使用的汇编器的几种方言外,多年来添加了附加的处理器指令,寄存器和其他功

  • Navicat 提供备注、十六进制、图像、网页或动态列窗格来查看和编辑 Text、Blob 或 BFile 字段内容。编辑器可让你在表中查看、更新、插入或删除数据。在工具栏点击 备注 十六进制 图像 网页 和 动态列 来 激活适当的查看器或编辑器。 注意:Oracle BFile 字段不可以编辑。 备注 窗格 让你编辑数据为一个简单的文本。要改变语法高亮显示,简单地在空白地方右击并选择 语言。使用

  • Navicat 提供强大的辅助编辑器来查看和编辑 TEXT、BLOB 或 BFile 字段的内容。编辑器可让你在表或集合中查看、更新、插入或删除数据。在工具栏点击 “文本”、“十六进制”、“图像”和 “网页”来打开相应的查看器或编辑器。 【注意】Oracle 的 BFile 字段不可以编辑。MongoDB JSON 视图不支持辅助编辑器。 “文本”窗格让你编辑数据为纯文本。若要更改语法高亮显示,简

  • Navicat 提供强大的辅助编辑器来查看和编辑 TEXT、BLOB 或 BFile 字段的内容。编辑器可让你在表或集合中查看、更新、插入或删除数据。在工具栏点击 “文本”、“十六进制”、“图像”和 “网页”来打开相应的查看器或编辑器。 【注意】MongoDB JSON 视图不支持辅助编辑器。Oracle 的 BFile 字段不可以编辑。 “文本”窗格让你编辑数据为纯文本。若要更改语法高亮显示,简

  • Navicat 提供强大的辅助编辑器来查看和编辑 TEXT、BLOB 或 BFile 字段的内容。编辑器可让你在表或集合中查看、更新、插入或删除数据。在工具栏点击 “文本”、“十六进制”和 “图像”来打开相应的查看器或编辑器。 【注意】Oracle 的 BFile 字段不可以编辑。MongoDB JSON 视图不支持辅助编辑器。 “文本”窗格让你编辑数据为纯文本。若要更改语法高亮显示,请简单地右击