Как сохранить значения флагов статуса процессора с соответствующим перечислением для 6502 - PullRequest
0 голосов
/ 28 сентября 2019

Я работаю над эмулятором 6502 в C ++ как часть моей диссертации.Он имеет 6 регистров, большинство из них просто содержат значения, но есть один особый - Статус процессора.Его ширина 8 бит, и каждый бит обозначает другой флаг.Мне показалось, что лучшим выбором было сделать это std :: bitset <8> и создать соответствующий класс enum для отображения его значений в реальные биты следующим образом:

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

Есть ли у вас какие-либо хорошие идеи о том, как добиться такой (или подобной) согласованности, не создавая в коде какие-то безумные или безобразные конструкции?

1 Ответ

0 голосов
/ 28 сентября 2019

То, что хочет ОП (или что-то подобное), может быть достигнуто с помощью вложенных struct с.

Ради интереса я попытался смоделировать то, что предполагал ОП:

#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;

Aнебольшой тест, чтобы показать это в действии:

#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:: не предоставляет деструктора virtual с соответствующими последствиями.В приведенном выше коде это не должно быть проблемой.


6502 напомнил мне о Commodore 64 , где я сделал свои первые попытки (хотяC64 имел еще более современный процессор 6510 .Однако, это очень давно ...; -)

...