-1

我正在学习汇编语言并被困在这一点上。这是“计算机系统”第3章中的一个问题。问题描述是:

问题的第一部分

问题的第二部分

看问题 A、B 和 C。

一种。

cmpl %eax, %edx
setl %al

解决方案:后缀“l”和寄存器标识符表示 32 位操作数,而比较是针对二进制补码“<”。我们可以推断出 data_t 必须是 int。

B.

cmpw %ax, %dx
setge %al

解决方案:后缀“w”和寄存器标识符表示 16 位操作数,而比较是针对二进制补码“>=”。我们可以推断出 data_t 一定很短。

C。

cmpb %al, %dl
setb %al

解决方案:后缀“b”和寄存器标识符表示 8 位操作数,而比较是针对无符号“<”。我们可以推断出 data_t 一定是 unsigned char。

我的问题是如何确定“比较适用于二进制补码'<'”,“比较适用于二进制补码'>='” 和“比较是针对无符号的'<'”。此外,我无法理解如何从中确定数据类型。

4

2 回答 2

2

第一部分(数据类型)很简单。eax是一个 32 位寄存器,所以数据类型是int(或更准确地说int32_t)。同样,ax是一个 16 位寄存器和al一个 8 位寄存器。

对于第二部分,您需要了解说明。英特尔规范说(在setxx命令下):

术语“上方”和“下方”与 CF 标志相关联,指的是两个无符号整数值之间的关系。术语“更大”和“更少”与 SF 和 OF 标志相关联,指的是两个有符号整数值之间的关系。

Sosetb对无符号值进行操作,而setland对有符号值setge进行操作。这里的“补码”与“有符号”的意思相同。

于 2022-02-02T19:42:44.253 回答
0

对于整数,数据类型的概念涉及宽度和符号。

数据类型用于变量,可以有符号或无符号。变量可以保存值,并且整数值可以是负数或正数。

汇编中有大约 4 种标准宽度,字节、字、长、四边形,每个都是前一个的两倍。这些不一定表明是签名还是未签名。在 x86 世界中,word是 16 位,而在大多数其他环境(MIPS/RISC V)word中是指 32 位。此外,long有时称为dword双字,qword用于 8 字节值。

C 中有 4 种标准宽度,char、short、int、long,但在 C 中,它们通常被理解为有符号的——除了char具有特定于实现的有符号性。C 保证 sizeof(char) < sizeof(short) <= sizeof(int) <= sizeof(long),但要确切知道哪个是您必须查阅实现文档的内容——实现应该告诉您。许多实现都具有int32long位,但有时有编译器选项可以更改它,并且long long通常是 64 位。

在 C 中,我们可以添加关键字signedorunsigned以确保数据类型分别是可以保存负值或不能保存负值的数据类型。

对于广泛跨有符号和无符号数据类型的比较操作,共有 10 个常用关系。现在让我们注意,编程语言和指令集省略了一个操作数有符号而另一个无符号的关系操作(反之亦然)。如果您遇到这种情况,最好的方法是将两个操作数提升到下一个更高的有符号大小并以这种方式进行比较。因此,包含在标准 10 关系中的是无符号到无符号和有符号到有符号的比较(但没有符号到无符号,也没有无符号到有符号)。

其中两个相等 (==,eq) 和不相等 (!=,ne),对有符号和无符号数据类型都应用相同 - 要相等或不同,位模式必须相同且有符号性不在那里很重要(假设两个操作数都是有符号的或都是无符号的)。

其余的,我们必须知道数据类型的有符号性才能正确解释结果。一个负数,如果不小心被视为无符号数,看起来就像一个很大的正数。所以,如果我们使用了错误的比较运算符,那么 -1 将显示为 maxint 并且大于 1。这就是我们必须知道数据类型的原因。我们可以从比较运算符推断数据类型是有符号还是无符号。

业界普遍采用了以下术语:

  • 无符号 > 和无符号 < 的上方和下方
  • 无符号 >= 和无符号 <= 高于或相同 & 低于或相同 (68000)
  • 高于或等于 & 低于或等于 unsigned >= 和 unsigned <= (Intel)
  • 对于无符号 < (MIPS/RISC V) 小于无符号 (ltu)
  • 小于 & 大于用于有符号 < 和有符号 >
  • 小于或等于 & 大于或等于有符号 <= 和有符号 >=

让我们还补充一点,C(和其他高级语言)使用逻辑变量声明来标记具有数据类型的变量,并且每当程序使用变量时,编译器生成的机器代码会以与该数据类型一致的方式访问相同变量的物理存储。

而在机器代码中,没有处理器看到或知道的变量声明,因此,必须在每条根据需要操作存储的指令中传达一些数据类型信息。要复制数据,处理器只需要知道大小,而不是有符号性,相同/不等比较,但对于其他操作(其他不等式,如 <、<= 或检测溢出)必须通知处理器数据类型的大小和有符号性。

处理器不读取变量声明至少有两个原因,一个是它们记忆太多,或者换句话说,我们有另一种记忆方式,即将这些信息合并到机器中程序代码,这意味着程序真正知道,并在每条指令处告诉处理器。

另一个是处理器的物理存储:CPU 寄存器和内存,经常被重新利用。CPU 寄存器是永久的,但高级语言的逻辑变量可能是短暂的——尤其是参数和局部变量。逻辑变量有作用域,当作用域退出时,变量消失,物理存储空间可以自由用于其他目的,汇编程序只需用新值初始化该物理存储空间即可。因此,某一时刻同一个寄存器可能保存一个无符号字节,另一时刻保存一个有符号整数。机器代码程序的工作是保持这一点,编译器部分地通过它正在翻译的源代码中的类型声明来做到这一点。


条件分支有点复杂,如下。在 C 中,我们可能会做类似的事情

if ( a < b ) goto Label;

这种相对简单的操作基本上有 4 个操作数,比大多数处理器在一条指令中容纳的要多。4 个操作数是变量 1、变量 2、特定的关系运算符和 goto-target 标签。

因此,指令集设计人员使用的一种方法是将 4 个操作数拆分并将它们分散到 2 个单独的指令中,例如compareand branchor set。比较操作取变量 1 和变量 2 并同时执行所有 10 个比较操作,将所有 10 个结果放入标志寄存器。这些branch指令采用特定的关系运算符和 goto-target 标签——它们解释给定关系运算符的标志,以查看它是否应该分支。

这些setxx指令与分支指令并行,但具有寄存器目标(布尔值)而不是 goto/branch 目标。

于 2022-02-02T20:13:14.437 回答