diff --git a/Marlin/src/core/macros.h b/Marlin/src/core/macros.h index 56250fb149..e3fd9b6609 100644 --- a/Marlin/src/core/macros.h +++ b/Marlin/src/core/macros.h @@ -64,7 +64,7 @@ // Macros for bit masks #undef _BV -#define _BV(n) (1<<(n)) +#define _BV(b) (1 << (b)) #define TEST(n,b) (!!((n)&_BV(b))) #define SET_BIT_TO(N,B,TF) do{ if (TF) SBI(N,B); else CBI(N,B); }while(0) #ifndef SBI diff --git a/Marlin/src/core/types.h b/Marlin/src/core/types.h index f5dc231289..4bc50b56c7 100644 --- a/Marlin/src/core/types.h +++ b/Marlin/src/core/types.h @@ -159,36 +159,90 @@ template struct IF { typedef L type; }; #define FI FORCE_INLINE // Define types based on largest bit width stored value required -#define bits_t(W) typename IF<((W)> 16), uint32_t, typename IF<((W)> 8), uint16_t, uint8_t>::type>::type +#define bits_t(W) typename IF<((W)> 32), uint64_t, typename IF<((W)> 16), uint32_t, typename IF<((W)>8), uint16_t, uint8_t>::type>::type>::type #define uvalue_t(V) typename IF<((V)>65535), uint32_t, typename IF<((V)>255), uint16_t, uint8_t>::type>::type #define value_t(V) typename IF<((V)>32767), int32_t, typename IF<((V)>127), int16_t, int8_t>::type>::type -// General Flags for some number of states +// Define a template for a bit field of N bits, using the smallest type that can hold N bits +template 64)> +struct Flags; + +// Flag bits for <= 64 states template -struct Flags { +struct Flags { typedef bits_t(N) flagbits_t; - typedef struct { bool b0:1, b1:1, b2:1, b3:1, b4:1, b5:1, b6:1, b7:1; } N8; - typedef struct { bool b0:1, b1:1, b2:1, b3:1, b4:1, b5:1, b6:1, b7:1, b8:1, b9:1, b10:1, b11:1, b12:1, b13:1, b14:1, b15:1; } N16; - typedef struct { bool b0:1, b1:1, b2:1, b3:1, b4:1, b5:1, b6:1, b7:1, b8:1, b9:1, b10:1, b11:1, b12:1, b13:1, b14:1, b15:1, - b16:1, b17:1, b18:1, b19:1, b20:1, b21:1, b22:1, b23:1, b24:1, b25:1, b26:1, b27:1, b28:1, b29:1, b30:1, b31:1; } N32; - union { - flagbits_t b; - typename IF<(N>16), N32, typename IF<(N>8), N16, N8>::type>::type flag; + flagbits_t b; + + class BitProxy { + public: + BitProxy(flagbits_t& data, int bit) : data_(data), bit_(bit) {} + + BitProxy& operator=(const bool value) { + if (value) + data_ |= (flagbits_t(1) << bit_); + else + data_ &= ~(flagbits_t(1) << bit_); + return *this; + } + + operator bool() const { return bool(data_ & (flagbits_t(1) << bit_)); } + + private: + flagbits_t& data_; + uint8_t bit_; }; + FI void reset() { b = 0; } FI void set(const int n, const bool onoff) { onoff ? set(n) : clear(n); } - FI void set(const int n) { b |= (flagbits_t)_BV(n); } - FI void clear(const int n) { b &= ~(flagbits_t)_BV(n); } - FI bool test(const int n) const { return TEST(b, n); } - FI bool operator[](const int n) { return test(n); } + FI void set(const int n) { b |= (flagbits_t(1) << n); } + FI void clear(const int n) { b &= ~(flagbits_t(1) << n); } + FI bool test(const int n) const { return bool(b & (flagbits_t(1) << n)); } + FI BitProxy operator[](const int n) { return BitProxy(b, n); } FI bool operator[](const int n) const { return test(n); } FI int size() const { return sizeof(b); } - FI operator bool() const { return b; } + FI operator bool() const { return b != 0; } +}; + +// Flag bits for more than 64 states +template +struct Flags { + uint8_t bitmask[(N+7)>>3]; + // Proxy class for handling bit assignment + class BitProxy { + public: + BitProxy(uint8_t data[], int n) : data_(data[n >> 3]), bit_(n & 7) {} + + // Assignment operator + BitProxy& operator=(const bool value) { + if (value) + data_ |= _BV(bit_); + else + data_ &= ~_BV(bit_); + return *this; + } + + // Conversion operator to bool + operator bool() const { return TEST(data_, bit_); } + + private: + uint8_t& data_; + uint8_t bit_; + }; + + FI void reset() { for (uint8_t b = 0; b < sizeof(bitmask); ++b) bitmask[b] = 0; } + FI void set(const int n, const bool onoff) { onoff ? set(n) : clear(n); } + FI void set(const int n) { bitmask[n >> 3] |= _BV(n & 7); } + FI void clear(const int n) { bitmask[n >> 3] &= ~_BV(n & 7); } + FI bool test(const int n) const { return TEST(bitmask[n >> 3], n & 7); } + FI BitProxy operator[](const int n) { return BitProxy(bitmask, n); } + FI bool operator[](const int n) const { return test(n); } + FI int size() const { return sizeof(bitmask); } + FI operator bool() const { for (uint8_t b : bitmask) if (b) return true; return false; } }; // Specialization for a single bool flag template<> -struct Flags<1> { +struct Flags<1, false> { bool b; FI void reset() { b = false; } FI void set(const int n, const bool onoff) { onoff ? set(n) : clear(n); } @@ -218,7 +272,7 @@ typedef struct { FI bool operator[](const int n) { return flags[n]; } FI bool operator[](const int n) const { return flags[n]; } FI int size() const { return sizeof(flags); } - FI operator bool() const { return flags; } + FI operator bool() const { return (bool)flags; } } AxisFlags; // diff --git a/Marlin/tests/core/test_types.cpp b/Marlin/tests/core/test_types.cpp index 865d35de15..d569386265 100644 --- a/Marlin/tests/core/test_types.cpp +++ b/Marlin/tests/core/test_types.cpp @@ -532,6 +532,59 @@ MARLIN_TEST(types, Flags_32) { TEST_ASSERT_EQUAL(4, flags.size()); } +MARLIN_TEST(types, Flags_64) { + Flags<64> flags; + + flags.reset(); + TEST_ASSERT_EQUAL(0, flags.b); + + flags.set(0, true); + flags.set(63, true); + TEST_ASSERT_EQUAL(9223372036854775809ULL, flags.b); + + flags.clear(0); + flags.clear(63); + TEST_ASSERT_EQUAL(0, flags.b); + + flags.set(0, true); + flags.set(63, true); + TEST_ASSERT_EQUAL(true, flags.test(0)); + TEST_ASSERT_EQUAL(true, flags.test(63)); + TEST_ASSERT_EQUAL(false, flags.test(1)); + TEST_ASSERT_EQUAL(false, flags.test(62)); + + TEST_ASSERT_EQUAL(true, flags[0]); + TEST_ASSERT_EQUAL(true, flags[63]); + TEST_ASSERT_EQUAL(false, flags[1]); + TEST_ASSERT_EQUAL(false, flags[62]); + + TEST_ASSERT_EQUAL(8, flags.size()); +} + +MARLIN_TEST(types, Flags_302) { + Flags<302> flags; + + flags.reset(); + TEST_ASSERT_EQUAL(false, (bool)flags); + + flags.set(0, true); + flags.set(301, true); + + TEST_ASSERT_EQUAL(true, (bool)flags); + + TEST_ASSERT_EQUAL(true, flags.test(0)); + TEST_ASSERT_EQUAL(true, flags.test(301)); + TEST_ASSERT_EQUAL(false, flags.test(1)); + TEST_ASSERT_EQUAL(false, flags.test(300)); + + TEST_ASSERT_EQUAL(true, flags[0]); + TEST_ASSERT_EQUAL(true, flags[301]); + TEST_ASSERT_EQUAL(false, flags[1]); + TEST_ASSERT_EQUAL(false, flags[300]); + + TEST_ASSERT_EQUAL(38, flags.size()); +} + MARLIN_TEST(types, AxisFlags_const_as_bools) { const AxisFlags axis_flags_const_false = {0}; TEST_ASSERT_FALSE(axis_flags_const_false);