I'm working on 6502 emulator in C++ as a part of my thesis. It has 6 registers, most of them just hold values but there's one special - Processor Status. It's 8 bit wide and each bit means a different flag. The best choice for me seemed to make it std::bitset<8> and create a corresponding enum class to map its values to the real bits as follow:
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)
{
}
};
And now, if I want to refer to one of three: size of PS, the flag number or the bitset itself I have:
Registers::PSSize; // size
PSFlags::Carry; // flag number
Registers r; r.PS; // bitset itself
Where every call accesses the value in a very different way. I'd like to have it more consistent, e.g.
Registers::PS::value; // for the bitset itself
Registers::PS::size; // for the size
Registers::PS::flags::Carry; // for the name of flag
Do you have any good ideas on how to achieve such (or similar) consistency without creating some crazy or ugly constructs in the code?
What OP wants (or something acceptable similar) can be achieved using nested
structs.Just for fun, I tried to model what OP intended:
A small test to show this in action:
Output:
Note:
About naming the nested struct
Registers::PSand the memberRegisters::PSwith same name is I was expecting to work. Though, usually I use uppercase start character for type identifiers and lowercase start characters for variables. Hence, I don't have this issue usually.As being in doubt about this, I tested the
struct Registersagainst various compilers (though I wouldn't count this as proof against the standard): Compiler ExplorerDeriving from
std::containers should be done with care (i.e. better not). Probably for performance reasons, none of thestd::containers provides avirtualdestructor with the respective consequences. In the above code, this shouldn't be a problem.6502 reminded me to the Commodore 64 where I made my first attempts on (although the C64 had the even more modern 6510 CPU). However, that's looong ago... ;-)