Search code examples
cstructcastingbit-fieldsuint8t

Turning a bitfield into a uint8_t


My bitfield below represents the 7 status flags for the 6502 CPU. I am trying to emulate the instruction php, which pushes a copy of the status flags onto the stack.

struct Flags {
    uint8_t C: 1;
    uint8_t Z: 1;
    uint8_t I: 1;
    uint8_t D: 1;
    uint8_t V: 1;
    uint8_t N: 1;
    uint8_t B: 2;
};

I need a way to pack each field into a single-byte datatype like uint8_t so that I can push it to the stack. However, the code below gives this error: operand of type 'struct Flags' where arithmetic or pointer type is required. How do I resolve this problem?

int main() {
    struct Flags f = {0, 0, 0, 0, 1, 0, 0};
    uint8_t f_as_byte = (uint8_t) f;
}

Solution

  • The problem with bitfields is that it is implementation-defined in what order the bits are laid out. This could be rather unacceptable for a 6502 emulator. The PHP command must push the status word in the exact desired format, i.e. something like

    uint8_t as_state = f.N << 7 | f.V << 6 | f.B << 4 | f.D << 3 | f.I << 2 | f.Z << 1 | f.C;
    

    Your layout is wrong, the B member is in wrong position. All in all considering the complex code like one above, maybe it would be easier to just consider the flags as a single uint8_t and have access macros for it, something like

    #define FLAG_N 0x80U
    ...
    #define FLAG_C 0x1U
    
    #define SET_FLAG(flag_var, flag) ((flag_var) |= (flag))
    #define CLR_FLAG(flag_var, flag) ((flag_var) &= ~(flag))
    #define GET_FLAG(flag_var, flag) ((_Bool)((flag_var) & (flag)))
    
    uint8_t flags = 0;
    SET_FLAG(flags, FLAG_C);
    
    if (GET_FLAG(flags, FLAG_N)) { ... }
    

    That way the PHP instruction can be coded to just push flags as is...

    Except that the B flag that is pushed is not a flag from the status register...