【问题标题】:How can I add two bytes in Assembly if the sum of these two bytes exceeds a byte?如果这两个字节的总和超过一个字节,如何在 Assembly 中添加两个字节?
【发布时间】:2020-10-11 17:08:10
【问题描述】:

我正在拼贴学习汇编语言,我很确定我什么都不懂,尤其是因为课程是在线的。

这可能是一个非常简单的问题,但我仍然不知道答案。假设我想添加两个存储为字节a = 255b = 255 的数字。如果我们这样做,我认为这是行不通的:

segment data use32 class=data
    a db 255
    b db 255

segment code use32 class=code
    start:
    
        mov al, [a]
        add al, [b]
    

由于总和是510,它不能存储在AL 中,因为AL 是一个1 字节大小的寄存器,对吧?我用 NASM 进行了尝试(我几乎不知道如何使用它),但我没有认为我得到了正确的答案。 EAX 寄存器最后看起来像这样0019FFFE,而 6 进制的 510 是 1FE,所以我想我应该在某个地方看到 1FE,但我们没有!

所以我想这不是正确的方法。然后我认为也许我可以使用AX 以某种方式进行添加。所以我做了这样的事情:

segment data use32 class=data
    a db 255
    b db 255

segment code use32 class=code
    start:

        mov ax, word 0
        mov al, [a]
        add al, [b]

我认为,如果我们将 AX 初始化为 0,然后向 AL 添加一些会导致值超过一个字节的值,则结果将通过 AX 传播,我们会得到正确答案。但我最后得到了001900FE,所以再一次没有1FE的踪迹。

那么我应该如何添加这两个值呢?如果这是一个愚蠢的问题,我很抱歉,但我真的很困惑。我几乎没有保留课堂上的任何内容,而且关于组装,互联网上的资源非常稀缺,所以我并没有真正在网上找到我的问题的答案。

【问题讨论】:

  • 你添加到al,这只是eax的最低8位。高位不受指令影响。是的,这是今天没有人想要的 x86 的一个可怕的错误功能,它的起源可以追溯到 x86 的 8 位前身(8008/8080/8085),因为即使在那时英特尔也很感激他们全新处理器的兼容性设计。如果你想加宽,你可以先加载到一个寄存器中,然后添加整个(16 位或 32 位)寄存器。

标签: assembly x86 byte nasm


【解决方案1】:

如果您尚未使用处理器处理的最大尺寸,那么您可以增加一个尺寸。

改为以 16 位进行加法。处理器通常不支持将 8 位值添加到 16 位答案中,但您可以将 8 位值提升为 16 位,然后再进行 16 位加法。

要扩展到 16 位,您可以将一个值加载到 al,然后清除 ah,所以现在 ax 拥有一个 16 位的值。在 bl/bh/bx 或 cl/ch/cx 中执行相同的操作,然后您可以将两个 16 位值相加。这是假设您的输入是无符号字节。 (如果它们是有符号字节,您将使用符号扩展操作将其提升为 16 位。)您也可以先清除 ax,然后加载到 al,然后您将有一个 16 位零扩展值ax.

如果您尝试做同样的事情,但您已经使用了处理器的最大大小,例如 32 位,您将在加法后检查进位标志,这将告诉您加法是否溢出或者没有(这意味着它适合 32 位)。

顺便说一句,进位标志也是通过8位加法设置的,所以你也可以检查一下结果是否大于8位。

通常,将两个 n 位值相加会产生一个 n+1 位的答案,而将两个 n 位值相乘会产生一个 2n 位的答案。在 x86 上,进位标志用作额外的位,乘法指令旨在提供更大的答案。

【讨论】:

  • OP 提到了 EAX;这意味着他们可以在兼容 386 的 CPU 上简单地 movzx eax, byte [a] / movzx ecx, byte [b] 而不是乱搞部分寄存器。
  • @ErikEidt 这消除了我的很多困惑,非常感谢。您说“您也可以先清除 ax,然后加载到 al,您将在 ax 中有一个 16 位零扩展值。”。我们是否正在清除 ax(因此我们将其设置为 0)以在程序启动之前将其从内存中可能存在的垃圾值中清空?我说对了吗?
  • 是的,通常我们在使用后不会清除寄存器,因此我们应该将它们视为具有未知值。通常,这不是问题,因为我们只是用新值加载寄存器,但零扩展需要一些零。此外,正如@PeterCordes 所提到的,您可以将它们一直提升到 32 位,并且有一条指令可以同时加载和零扩展。
  • @ErikEidt 如果您不介意回答,那就再混淆一次。你说“如果你试图做同样的事情,但你已经在使用......它会告诉你加法是否溢出或没有(这意味着它适合 32 位)。”如果我溢出怎么办?如果我们已经使用最大的寄存器,如 EAX 和 EBX,有没有办法解决并找到实际总和。另外,假设我清除 AX 和 BX,用 8 位值填充 AL 和 BL,然后将 AX 和 BX 相加。如果总和超过 8 位,进位标志还会被设置吗?即使我使用 16 位寄存器 AX 和 BX 进行加法?
  • 进位位是第 33 位。对于 32 位加法的 33 位答案,没有溢出的可能性。只有当我们尝试丢弃第 33 位,只保留低 32 位时,才有可能溢出,进位标志会告诉我们什么时候真正的数值需要第 33 位(为 1),这也是意味着丢弃高位第 33 位将导致溢出,因为 32 位中的剩余数值将更改/将不正确。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-17
  • 2016-01-07
  • 1970-01-01
  • 1970-01-01
  • 2020-09-22
相关资源
最近更新 更多