Based on the answer of @chqrlie I ended up with following simplification that basically solves what I wanted to solve:
if (!Is_sup_sz(SZ_L(d))) { return EXEC_ERROR_INVALID_OP_SZ; }
switch (LAYOUT(d)) {
case Ops_layout_Reg2:
Reg_set(®_L(s,d), ®_R(s,d), SZ_L(d));
break;
case Ops_layout_RegImm:
Reg_set(®_L(s,d), &IMM1(d), SZ_L(d));
break;
case Ops_layout_Imm2:
case Ops_layout_ImmReg:
default: return EXEC_ERROR_INVALID_3ADDR_OPS_LAYOUT;
}
return EXEC_CORRECT;
where 2 additional functions resolve what part of Cell16
(renamed to just Cell
) is to be modified and what operand size is correct to be processed using compile time constant:
#define SUP_SZ_MASK (uint8_t)0x18 // 0b00011000 only 8/16 bit.
inline bool Is_sup_sz(Op_sz sz) {
return (sz != 0) && ((sz & (sz - 1)) == 0) && ((sz & SUP_SZ_MASK) != 0);
}
inline void Reg_set(Cell *dest, const Cell *src, Op_sz sz) {
// Formula guarantees no overflow even for uint64_t.
uint16_t mask_right = (((1 << (sz - 1)) - 1) << 1) + 1,
mask_left = ~mask_right;
// Only work with widest value of Cell.
dest->sz16 &= mask_left;
dest->sz16 |= (src->sz16 & mask_right);
}
I decided to not replace union Cell
with just uint16_t
because I want variables that model registers to be addressable as real registers are like (RAX/EAX)/AX/AL (no AH tho).