Reading CF, PF, ZF, SF, OF
Reading CF, PF, ZF, SF, OF
我正在为自己的汇编语言编写一个虚拟机,当我执行加法等操作时,我希望能够像在x86-64体系结构中设置的那样设置进位、奇偶校验、零、符号和溢出标志。
注:
- 我正在使用Microsoft Visual C++2015&英特尔C++编译器16.0
- 我正在作为Win64应用程序进行编译
- 我的虚拟机(目前)只对8位整数进行算术运算
- 我(目前)对任何其他标志(如AF)都不感兴趣
我目前的解决方案是使用以下功能:
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);
}
作为补充,我会使用如下:
uint8_t a, b;
update_flags(uint16_t(a) + uint16_t(b));
uint8_t c = a + b;
编辑:为了澄清,我想知道是否有更有效/更简洁的方法(例如直接访问RFLAGS)此外,我的代码可能不适用于其他操作(如乘法)
编辑2我现在已经将代码更新为:
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)的实现,因此我希望这些标志的行为基本相同。
TL:DR:使用惰性标志评估,请参阅下文。
input
是个奇怪的名字。大多数ISAs更新标志基于操作的结果,而不是输入。您看到的是8位运算的16位结果,这是一种有趣的方法。在C中,您应该只使用unsigned int
,它保证至少是uint16_t
。它将在x86上编译成更好的代码,其中unsigned
是32位。16位操作需要一个额外的前缀,并且可能导致部分寄存器速度减慢。
这可能有助于解决您提到的8bx8b->16b-mul问题,具体取决于您希望如何在模拟的体系结构中定义mul指令的标志更新。
我认为您的溢出检测不正确。请参阅从x86标记wiki链接的本教程,了解如何完成。
这可能不会编译成非常快的代码,尤其是奇偶校验标志。您是否需要模拟/设计的ISA具有奇偶校验标志?你从来没有说过你在模仿x86,所以我认为这是你自己设计的玩具架构。
一个高效的模拟器(尤其是需要支持奇偶校验标志的模拟器)可能会从某种惰性标志评估中受益匪浅。保存一个值,如果需要,可以从中计算标志,但在到达读取标志的指令之前,不要实际计算任何内容。大多数指令只写标志而不读标志,它们只是将uint16_t
结果保存到您的体系结构状态中。标志读取指令可以从保存的uint16_t
中只计算出所需的标志,也可以计算出所有标志并以某种方式存储。
假设您无法让编译器从结果中实际读取PF
,那么您可以尝试_mm_popcnt_u32((uint8_t)x) & 1
。或者,将所有位水平异或在一起:
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 al
或PUSHF
。例如,可以引导编译器使用标志条件来检测整数溢出,以实现饱和加法。但是,让它弄清楚你想要所有的标志,并实际使用LAHF
而不是一系列setcc
指令,可能是不可能的。编译器需要一个模式识别器来确定何时可以使用LAHF
,但可能没有人实现,因为用例非常罕见。
没有C/C++方法可以直接访问操作的标志结果,这使得C在实现这样的东西时是一个糟糕的选择。IDK,如果任何其他语言都有标志结果,而不是asm。
我希望您可以通过在asm中编写部分仿真来获得很大的性能,但这将是特定于平台的。更重要的是,这是更多的工作。
我似乎已经解决了这个问题,通过将更新标志的参数拆分为无符号和有符号的结果,如下所示:
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
}
对于加法(应该为有符号和无符号输入都产生正确的结果),我会做以下操作:
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;
有符号乘法我会做以下操作:
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
解决方案应该可以工作。。
附言:感谢所有回应的人,这很有帮助。此外,如果有人能在我的代码中发现任何错误,我们将不胜感激。
- Usages of std::move
- 使用 [] 运算符时"binding reference of type discards qualifiers"
- 如何处理 c++ 中类实现中的"invalid use of non-static data member"?
- 具有变量Number of Arguments的std::函数的矢量
- Capacity of a deque
- Deprecation of _writeBarrier()
- constexpr begin of a std::array
- 如何调整 std::vector of Eigen::MatrixXd 的大小
- C++ "error: invalid use of void expression"
- 我看到"use of undeclared identifier"错误,有人可以告诉我如何解决它吗?
- 赛通"Cannot take address of memoryview slice"
- C++ Version Of Double.longBitsToDouble
- 收到错误"invalid use of non-static data member 'stu::n' "
- C++ Usage of AsyncCallback
- 错误:"Left of getValue must have class/struct/union"
- Centos7 g++ "to_string is not in a member of std"
- 在C++中使用 Catch 测试框架编译错误"error: expected ';' at end of declaration list"
- 传递 std::vector of std::shared_ptr,而不是更新对象
- 尝试使用 SFLM 顶点时出错:"Vertex is not a member of sf"
- Reading CF, PF, ZF, SF, OF