首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >阅读CF,PF,ZF,SF

阅读CF,PF,ZF,SF
EN

Stack Overflow用户
提问于 2016-03-26 05:33:14
回答 2查看 1.7K关注 0票数 0

我正在为我自己的汇编语言编写一个虚拟机,当我执行诸如加法之类的操作时,我希望能够设置在x86-64架构中设置的进位、奇偶、零、符号和溢出标志。

备注:

  • 我正在使用微软的VisualC++ 2015 &英特尔的C++编译器16.0
  • 我正在编译为一个Win64应用程序。
  • 我的虚拟机(目前)只对8位整数进行算术。
  • 我(目前)对任何其他旗帜(如AF)不感兴趣。

我目前的解决方案是使用以下函数:

代码语言:javascript
复制
void update_flags(uint16_t input)
{
    Registers::flags.carry = (input > UINT8_MAX);
    Registers::flags.zero = (input == 0);
    Registers::flags.sign = (input < 0);
    Registers::flags.overflow = (int16_t(input) > INT8_MAX || int16_t(input) < INT8_MIN);

    // I am assuming that overflow is handled by trunctation
    uint8_t input8 = uint8_t(input);
    // The parity flag
    int ones = 0;
    for (int i = 0; i < 8; ++i)
        if (input8 & (1 << i) != 0) ++ones;

    Registers::flags.parity = (ones % 2 == 0);
}

此外,我还将如下所述:

代码语言:javascript
复制
uint8_t a, b;
update_flags(uint16_t(a) + uint16_t(b));
uint8_t c = a + b;

编辑:为了澄清,我想知道是否有一种更有效、更整洁的方法(比如直接访问RFLAGS ),我的代码也可能不适用于其他操作(例如乘法)。

编辑2我现在已经更新了我的代码如下:

代码语言:javascript
复制
void update_flags(uint32_t result)
{
    Registers::flags.carry = (result > UINT8_MAX);
    Registers::flags.zero = (result == 0);
    Registers::flags.sign = (int32_t(result) < 0);
    Registers::flags.overflow = (int32_t(result) > INT8_MAX || int32_t(result) < INT8_MIN);
    Registers::flags.parity = (_mm_popcnt_u32(uint8_t(result)) % 2 == 0);
}

还有一个问题,我的进位标志代码能正常工作吗?我还想要正确地为减法过程中发生的“借”设置代码。

注意:我正在虚拟化的汇编语言是我自己的设计,它的设计很简单,基于英特尔的x86-64实现(即Intel64),所以我希望这些标志的行为方式大致相同。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-03-26 12:48:05

我似乎已经解决了这个问题,将参数拆分为更新标志为未签名和签名的结果如下:

代码语言:javascript
复制
void update_flags(int16_t unsigned_result, int16_t signed_result)
{
    Registers::flags.zero = unsigned_result == 0;
    Registers::flags.sign = signed_result < 0;
    Registers::flags.carry = unsigned_result < 0 || unsigned_result > UINT8_MAX;
    Registers::flags.overflow = signed_result < INT8_MIN || signed_result > INT8_MAX
}

此外(这将为已签名和无符号的输入产生正确的结果),我将执行以下操作:

代码语言:javascript
复制
int8_t a, b;
int16_t signed_result = int16_t(a) + int16_t(b);
int16_t unsigned_result = int16_t(uint8_t(a)) + int16_t(uint8_t(b));
update_flags(unsigned_result, signed_result);
int8_t c = a + b;

我要做以下几件事:

代码语言:javascript
复制
int8_t a, b;
int16_t result = int16_t(a) * int16_t(b);
update_flags(result, result);
int8_t c = a * b;

等等,用于更新标志的其他操作。

注:我在这里假设int16_t(a)符号扩展,int16_t(uint8_t(a))零扩展。

我也决定不使用奇偶校验标志,如果我以后改变主意,我的_mm_popcnt_u32解决方案应该能工作。

谢谢大家的回复,这很有帮助。另外,如果有人能发现我的代码中的任何错误,那将是非常感谢的。

票数 0
EN

Stack Overflow用户

发布于 2016-03-26 06:32:56

TL:博士:使用惰性标志评估,见下文。

input是个奇怪的名字。大多数ISAs根据操作的结果更新标志,而不是根据输入。您将看到8位操作的16位结果,这是一种有趣的方法。在C中,您应该只使用unsigned int,这至少保证是uint16_t。它将在x86上编译成更好的代码,其中unsigned是32位。16位操作需要额外的前缀,并可能导致部分寄存器减速。

这可能有助于解决您注意到的8bx8b->16b mul问题,这取决于您想要如何定义要仿真的体系结构中的mul指令的标志更新。

我不认为你的溢出检测是正确的。有关如何实现,请参阅从本教程标记wiki链接而来的x86

这可能不会编译到非常快的代码,特别是奇偶校验标志。您是否需要您正在模拟/设计的ISA有一个奇偶标记?你从没说过你在模仿x86,所以我想这是你自己设计的玩具架构。

有效的仿真器(尤指)一个需要支持奇偶校验标志的人)可能会从某种惰性标志评估中获益良多。保存一个可以根据需要计算标志的值,但在到达读取标志的指令之前,不要实际计算任何内容。大多数指令只写标志而不读取标志,它们只将uint16_t结果保存到您的体系结构状态。读取标志的指令既可以从保存的uint16_t中计算所需的标志,也可以计算所有的标记并以某种方式存储。

假设无法让编译器从结果中实际读取PF,则可以尝试_mm_popcnt_u32((uint8_t)x) & 1。或者,水平XOR将所有比特放在一起:

代码语言:javascript
复制
x  = (x&0b00001111) ^ (x>>4)
x  = (x&0b00000011) ^ (x>>2)
PF = (x&0b00000001) ^ (x>>1)   // tweaking this to produce better asm is probably possible

我怀疑任何一个主要的编译器都不能窥视LAHF + SETO alPUSHF中对结果的一串检查。编译器可以导入到使用标志条件检测整数溢出以实现饱和加法,例如中。但是,让它知道您需要所有的标志,并实际使用LAHF而不是一系列的setcc指令,这可能是不可能的。编译器在何时可以使用LAHF时需要一个模式识别器,而且可能没有人实现这一点,因为用例非常罕见。

没有C/C++方法可以直接访问操作的标记结果,这使得C对于实现这样的操作来说是一个糟糕的选择。如果除asm之外,任何其他语言都有标志结果,则为IDK。

我希望您可以通过在asm中编写部分仿真来获得很大的性能,但这将是特定于平台的。更重要的是,这是更多的工作。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/36231958

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档