在模拟器中使用union来模拟CPU寄存器有多合适?

How appropriate is it to use a union to simulate CPU registers in an emulator?

所以我之前看到有人说以这种方式使用联合是个坏主意。我知道这在技术上是未定义的行为。但是,如果我使用的是 C++11(因此只有新的编译器),那么 老实说 下面的代码到底有多糟糕?这真的有可能让我大吃一惊吗?可以改进吗?

union registers_t
{
    struct [[packed]]
    {
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
        uint8_t F, A, C, B, E, D, L, H, R, I;
#else
        uint8_t A, F, B, C, D, E, H, L, I, R;
#endif
        uint16_t SP, PC;
    };
    struct [[packed]]
    {
        uint16_t AF, BC, DE, HL;
    };
};

就像我说的,我知道这是 C++ 中的 UB,所以没有理由指出这一点。我的问题是 在这种情况下这真的重要吗?

如果您写入联合成员,然后作为联合的不同成员访问相同数据,则行为未定义,除非其中一种类型是 char 类型.

所以这段代码似乎没问题。如果您尝试使用 32 位寄存器进行同样的操作(如果您尝试模拟 x86 寄存器,这将是不正确的,但这不是问题所在),那么写入 16 位元素并作为 32 位元素读回将是未定义的行为.

顺便说一句。 memcpy、memset 和 memmove 以字节形式访问数据。正式。

如果我理解正确,这仅适用于模拟器,所以您实际上不需要 8 位和 16 位值占用相同的存储空间,您只需要对相同数据的不同访问。

我的建议是将您的寄存器封装在 class 中(例如存储单个 16/32/64 值的 register_t class,然后您可以访问该值8/16/32 位)。根据您的需要,您甚至可以 return 代理对象作为一种参考,允许您分配寄存器的一部分(例如能够做到 reg.l() += 1)。

一个简单的示例可能如下所示:16 bit register_t with proxy reference to constituent bytes(其他运算符、位宽和 const 正确性留作 reader 的练习)。这样做的另一个好处(除了它不是 UB)是你不需要关心字节序。

如果你也想为你的寄存器取一个正确的名字,你可以将这些寄存器中的一些封装在另一个 class 中,它通过适当命名的成员函数提供对命名寄存器的访问。