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

ASM如何知道算术运算是有符号的还是无符号的?

萧建木
2023-03-14

我正在使用MASM 14.0进行组装,我与下面代码的输出混淆。

TITLE Exercise 4 from chapter 4

; Author : Saad Ahmed

INCLUDE Irvine32.inc

.code
main PROC

mov eax, 0
mov al, 255
add al, 1
call DumpRegs       ; Display registers

mov al, 127
add al, 1
call DumpRegs       ; Display registers

exit
main ENDP
END main

这两个算术运算都是在无符号整数255和127上完成的。

然而,CPU将第一个操作255视为无符号整数,并设置进位标志,当将1添加到无符号255时会出现这种情况。

完整的状态标志是CF=1 SF=0 ZF=1 OF=0 AF=1 PF=1,eax为0

但是,第二个操作在设置溢出标志时将127视为有符号整数,如果将127加1,则会发生溢出标志。

完整的状态标志是CF=0 SF=1 ZF=0 OF=1 AF=1 PF=0,eax为0。

问题是CPU如何决定第一个操作是在无符号255上完成的,而另一个操作是在有符号整数上完成的?

共有2个答案

杜嘉木
2023-03-14

二的补码的优点是CPU不需要知道。

它只对无符号字节进行加法。

设置进位标志是因为255 1不适合8位(显然)。

设置溢出标志是因为两个操作数的MSB为0,输出的MSB为1——表明如果您认为这是有符号算术,则表示您已经溢出。如果您认为它是无符号算术,那么您应该忽略该标志。请参阅这篇维基百科文章。

尉迟华翰
2023-03-14

对于使用二补码的加法(和减法),就逻辑而言,没有符号或无符号的概念。乘和除,是的,由于需要符号扩展。

取从000到111的所有3位数字的组合,将它们添加到所有3位数字的组合中,这是可以管理的。如果你愿意,可以写一个程序,也可以手工编写。或者只做角落案例。对于每个操作数,使用两个补码将每个操作数检查为有符号或无符号。您将注意到,相同的加法也有效。1110=111。现在是1(-2)=-1还是16=7。两者都起作用了。

进位标志是无符号溢出,V标志是有符号溢出,这就是为什么我们同时计算这两个数字,这样用户就可以选择正确的条件值,谁知道这些数字是有符号还是无符号。这就是为什么当大于或等于时有符号跳转,而当大于或等于时有无符号跳转。

正是两人互补的美使这一切都起了作用。

乘法(和除法)是不同的,因为你必须签署扩展。二进制乘法有一个漂亮的特性,如果你仔细想想

   abcd
*  0011
=======
   abcd
  abcd
 0000
0000
=======

位是1或0,所以你要把顶部的数字乘以1或0,你要么加它移位,要么不加。但是也要注意,你很快就会溢出。我们从小学就知道n^x*n^y=n^(x y)。如果你的寄存器是Z位宽,那么操作数的最高有效位位置在相加时不能大于Z,否则它会溢出。四位0010*0010应该可以,但是0010*1000会溢出。正确的方法是结果是操作数的两倍宽。

如果我想乘以1111*0010呢?

这基本上是

    0000
   1111
  0000
+0000
========
 0011110

等等,那是15(0b1111)还是-1(0b1111)?-1 * 2 = -2这不是我们上面得到的,我们做了一个无符号乘法来做一个有符号乘法,我们必须签署扩展并从左边抛出位

 11..1111
*00..0010
=========    
00000000
1111111
000000
00000
=========
11111110

这给出了两个四位寄存器1111和0010的有符号乘法的正确答案。

加法之所以有效,是因为您只关心一列。每列有一个进位、两个操作数、一个结果和一个进位。然后你可以把它级联到你想要的任何宽度。对于一个位,你有0和1。0只是零,不是正负零,1可以是1或-1。我发现通过多个列的组合更容易,但这是可以做到的。此外,进位是0,所以我不需要表示它操作数a、操作数b、进位和结果

00 00  0 + 0 = 0
01 01  0 + 1 = 1;  0 + (-1) = -1
10 01  1 + 0 = 1;  (-1) + 0 = -1
11 10  1 + 1 = 0 unsigned overflow.  -1 + 1 = 0, 1 + -1 = 0, -1 + -1 = 0 signed overflow

从技术上讲,在最后一种情况下,所有有符号的溢出都是有符号的溢出,这是您处理任意数量位的特殊情况,取一个三位寄存器100 100 = 000执行1是4 4=0无符号溢出还是有符号溢出的-4 -4 = 0?这就是为什么当您使用几个位并通过抛出1然后全零的问题情况的组合时更容易看到。

有符号溢出是当最重要列的进位与进位不匹配时。无符号溢出是当msbit的进位为1时。

逻辑中的减法是用加法来完成的,我们从小学就知道a-b=a(-b),我们从编程课上知道,要用两个补码取负数,就要倒过来加一。这很好,我们可以反转第二个操作数,反转lsbit的进位,使其成为一,或者反转并相加一。这就是工作原理。执行有时从alu中反转出来,以指示借用。你必须看看标志组合才能弄清楚这一点,一些处理器反转执行一些不执行。有时可以从带借位的减法中判断ISA是否有该指令。

我知道TL:DR…比你要求的要多。

 类似资料:
  • 我在看的书:CS-app 2。c有无符号和有符号的int类型,并且在大多数架构中使用二进制补码算法来实现有符号值;但是学了一些汇编代码之后,发现很少有指令区分无符号和有符号。所以我的问题是: > 区分有符号和无符号是编译器的责任吗?如果是,它是如何做到的? 谁实现两个补码算法——CPU还是编译器? 添加更多信息: 在学习了更多的指令后,实际上有一些指令区分有符号和无符号,例如setg、seta等。

  • 有符号和无符号变量在按位运算上有区别吗?< br >例如,在处理无符号数字时:< br> 将得到00000101。 但当处理带符号的数字时会发生什么?

  • C语言有符号和无符号类型,如char和int。我不确定它是如何在程序集级别实现的,例如,在我看来,有符号和无符号的乘法会带来不同的结果,那么程序集是同时做无符号和有符号的算术,还是只做一个,这在某种程度上是针对不同情况模拟的?

  • 我正在设计一个简单的玩具指令集和附带的仿真器,并试图找出支持什么指令。在算术方面,我目前有无符号加法、减法、乘法和除法。然而,对于以下问题,我似乎找不到一个明确的答案:哪种算术运算符需要有符号版本,而无符号和二的补码有符号版本对哪一种是等价的? 例如,1111在2的补码中等于-1。如果你给它加1,假装它是一个无符号的数字,你会得到0000,即使把它想象成-1也是正确的。然而,这适用于所有数字吗?其

  • 考虑函数调用(调用< code>int sum(int,int)) 编译器如何确定函数调用 中使用的 不是逗号运算符? 注意:我不想在函数调用中实际使用逗号运算符。我只是想知道编译器如何知道它不是逗号运算符。

  • 问题内容: 我了解Java中的无符号右移运算符“ >>>”是什么,但是为什么我们需要它,为什么我们不需要相应的无符号左移运算符? 问题答案: 该运营商允许你将和为32位和64位 无符号 整型,这是从Java语言缺少的。 当您移动不代表数值的内容时,这很有用。例如,您可以使用32位s 表示黑白位图图像,其中每个位图在屏幕上编码32个像素。如果需要将图像向右滚动,则希望将an左侧的位变为零,以便可以轻