I am working on a gameboy emulator. The gameboy CPU uses 16-bit registers whose upper and lower bytes can be accessed and operated on individually as well as the entire 16-bits. How do I implement it to work regardless of the difference in endianness across machines?
This is my current implementation
typedef union register16_t
{
struct
{
uint8_t lo, hi;
} reg;
uint16_t reg16;
} register16_t;
I realized that this is not portable because of difference in endianness between machines and started looking for solutions only to get more confused after going through various posts regarding whether the C standard supports type punning using unions. Now I am also confused about whether this kind of implementation is good practice or not.
Yes, while union
is a simplification, as you see, the endianess of the machine differs. You are emulating your data, there is no reason to depend on any machine specific behavior you are running on. Just write your own abstractions. You could do something along:
struct reg16 { uint16_t v; };
uint16_t reg16_get(const struct reg16 *t) { return t->v; }
void reg16_set(struct reg16 *t, uint16_t v) { t->v = v; }
uint8_t reg16_get_hi(const struct reg16 *t) { return t->v >> 8; }
uint8_t reg16_get_lo(const struct reg16 *t) { return t->v; }
void reg16_set_hi(struct reg16 *t, uint8_t v) { t->v = v << 8 | reg16_get_lo(t); }
void reg16_set_lo(struct reg16 *t, uint8_t v) { t->v = reg16_get_hi(t) << 8 | v; }
And use the functions (abstraction) whenever you need the data. You could also change the representation from uint16_t
to uint8_t[2]
you feel like it better without changing other parts of the code.