如何使用 6502 的相应枚举存储处理器状态标志的值

How to store Processor Status flags' values with corresponding enum for 6502

作为我论文的一部分,我正在使用 C++ 开发 6502 仿真器。它有 6 个寄存器,其中大部分只保存值,但有一个特殊的 - 处理器状态。它有 8 位宽,每一位代表一个不同的标志。对我来说最好的选择似乎是 std::bitset<8> 并创建相应的枚举 class 以将其值映射到实际位,如下所示:

enum class PSFlags : uint8_t
{
    Carry = 0,
    Zero = 1,
    InterruptDisable = 2,
    Decimal = 3,
    Break = 4,
    Unknown = 5,
    Overflow = 6,
    Negative = 7
};

struct Registers
{
    int8_t A;
    int8_t X;
    int8_t Y;
    uint8_t SP;
    uint16_t PC;
    static constexpr uint8_t PSSize = 8;
    std::bitset<PSSize> PS;

    constexpr Registers() noexcept :
        A(0),
        X(0),
        Y(0),
        SP(0xFF),
        PS(0b00100100),
        PC(0)
    {
    }
};

现在,如果我想引用三个之一:PS 的大小、标志编号或位集本身,我有:

Registers::PSSize;  // size
PSFlags::Carry;     // flag number
Registers r; r.PS;   // bitset itself

每次调用都以非常不同的方式访问值。我想让它更一致,例如

Registers::PS::value;        // for the bitset itself
Registers::PS::size;         // for the size
Registers::PS::flags::Carry; // for the name of flag

对于如何在不在代码中创建一些疯狂或丑陋的结构的情况下实现这种(或类似的)一致性,您有什么好的想法吗?

OP 想要的东西(或可接受的类似东西)可以使用嵌套的 structs 来实现。

为了好玩,我尝试模拟 OP 的意图:

#include <bitset>

struct Registers
{
    int8_t A;
    int8_t X;
    int8_t Y;
    uint8_t SP;
    static constexpr uint8_t PSSize = 8;

    struct PS: std::bitset<PSSize> {
      enum Flags {
        Carry = 0,
        Zero = 1,
        InterruptDisable = 2,
        Decimal = 3,
        Break = 4,
        Unknown = 5,
        Overflow = 6,
        Negative = 7
      };
      static constexpr unsigned Size = PSSize;

      constexpr PS(std::uint8_t value):
        std::bitset<PSSize>((unsigned long long)value)
      { }
      std::uint8_t value() const { return (std::uint8_t)to_ulong(); }
    } PS;

    uint16_t PC;

    constexpr Registers() noexcept :
        A(0),
        X(0),
        Y(0),
        SP(0xFF),
        PS(0x24),//PS(0b00100100),
        PC(0)
    {
    }
} r;

一个小测试来展示这一点:

#include <iomanip>
#include <iostream>

#define DEBUG(...) std::cout << #__VA_ARGS__ << ";\n"; __VA_ARGS__ 

int main()
{
  std::cout << std::hex << std::setfill('0');
  DEBUG(std::cout << Registers::PS::Flags::Carry << '\n');
  DEBUG(std::cout << r.PS[Registers::PS::Flags::Carry] << '\n');
  DEBUG(std::cout << Registers::PS::Flags::InterruptDisable << '\n');
  DEBUG(std::cout << r.PS[Registers::PS::Flags::InterruptDisable] << '\n');
  DEBUG(std::cout << Registers::PS::Flags::Break << '\n');
  DEBUG(std::cout << r.PS[Registers::PS::Flags::Break] << '\n');
  DEBUG(std::cout << Registers::PS::Size << '\n');
  DEBUG(std::cout << "0x" << std::setw(2) << (unsigned)r.PS.value() << '\n');
  // done
  return 0;
}

输出:

std::cout << Registers::PS::Flags::Carry << '\n';
0
std::cout << r.PS[Registers::PS::Flags::Carry] << '\n';
0
std::cout << Registers::PS::Flags::InterruptDisable << '\n';
2
std::cout << r.PS[Registers::PS::Flags::InterruptDisable] << '\n';
1
std::cout << Registers::PS::Flags::Break << '\n';
4
std::cout << r.PS[Registers::PS::Flags::Break] << '\n';
0
std::cout << Registers::PS::Size << '\n';
8
std::cout << "0x" << std::setw(2) << (unsigned)r.PS.value() << '\n';
0x24

注:

关于命名嵌套结构 Registers::PS 和具有相同名称的成员 Registers::PS 是我期望的工作。不过,我通常使用大写字母作为类型标识符的起始字符,使用小写字母作为变量的起始字符。所以我一般不会遇到这个问题。

由于对此有疑问,我针对各种编译器测试了 struct Registers(尽管我不认为这是对标准的证明):Compiler Explorer

std:: 容器派生应该小心(即最好不要)。可能出于性能原因,std:: 容器的 none 提供了具有相应后果的 virtual 析构函数。在上面的代码中,这应该不是问题。


6502 reminded me to the Commodore 64 where I made my first attempts on (although the C64 had the even more modern 6510CPU)。然而,那是很久以前的事了……;-)