* a class Bitfield that does the general bit handling. * a utility function generateBitfield that simplifies bitfield creation * function macros KEYMAP_BITFIELD and KEYMAP_BITFIELD_STACKED that enable definitions of keymap bitfields in the same way as keymaps are defined All exported functions live in namespace kaleidoscope::bitfields. Please note, that KEYMAP_BITFIELD is only defined if a keyboard hardware defines a KEYMAP_GENERIC macro and KEYMAP_BITFIELD_STACKED only if the hardware header defines KEYMAP_STACKED_GENERIC (not all keyboard hardwares define both). Expected side effects: As all symbols and macros are newly introduced, nothing is expected to break. Signed-off-by: Jesse Vincent <jesse@keyboard.io>pull/645/head
parent
ea052183be
commit
f764a78ecd
@ -0,0 +1,53 @@
|
||||
/* Kaleidoscope - Firmware for computer input devices
|
||||
* Copyright (C) 2013-2018 Keyboard.io, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "Kaleidoscope.h"
|
||||
|
||||
#include "bitfields.h"
|
||||
|
||||
namespace kaleidoscope {
|
||||
namespace bitfields {
|
||||
namespace internal {
|
||||
|
||||
bool _BaseBitfield::isBitSetP(const void *bit_field, uint8_t raw_pos) {
|
||||
uint8_t byte_id = raw_pos >> 3;
|
||||
uint8_t bit_pos = raw_pos & 0x7;
|
||||
const uint8_t *bytes = reinterpret_cast<const uint8_t *>(bit_field);
|
||||
return bytes[byte_id] & (0x1 << bit_pos);
|
||||
}
|
||||
|
||||
void _BaseBitfield::setBitP(void *bit_field, uint8_t raw_pos, uint8_t val) {
|
||||
uint8_t byte_id = raw_pos >> 3;
|
||||
uint8_t bit_pos = raw_pos & 0x7;
|
||||
uint8_t *bytes = reinterpret_cast<uint8_t *>(bit_field);
|
||||
if (val) {
|
||||
bytes[byte_id] |= (0x1 << bit_pos);
|
||||
} else {
|
||||
bytes[byte_id] &= ~(0x1 << bit_pos);
|
||||
}
|
||||
}
|
||||
|
||||
bool _BaseBitfield::isBitSetPROGMEM_P(const void *bit_field, uint8_t raw_pos) {
|
||||
uint8_t byte_id = raw_pos >> 3;
|
||||
uint8_t bit_pos = raw_pos & 0x7;
|
||||
const uint8_t *bytes = reinterpret_cast<const uint8_t *>(bit_field);
|
||||
uint8_t byte = pgm_read_byte(&(bytes[byte_id]));
|
||||
return byte & (0x1 << bit_pos);
|
||||
}
|
||||
|
||||
} // end namespace internal
|
||||
} // end namespace bitfields
|
||||
} // end namespace kaleidoscope
|
@ -0,0 +1,200 @@
|
||||
/* Kaleidoscope - Firmware for computer input devices
|
||||
* Copyright (C) 2013-2018 Keyboard.io, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace kaleidoscope {
|
||||
namespace bitfields {
|
||||
|
||||
namespace internal {
|
||||
|
||||
// To store the bitfield we use a recursively defined template struct.
|
||||
// It contains one byte of storage and a nested template struct
|
||||
// that wraps the remaining bytes. A specialization of the template
|
||||
// is used to end the recursion.
|
||||
//
|
||||
// By tagging the struct with __attribute__((packed)), we make sure
|
||||
// that no padding bytes are added between the struct instances
|
||||
// if the struct is used on non-8 bit architectures. This is important
|
||||
// to ensure that all bytes are stored contiguously like it is the
|
||||
// case in an intrinsic array.
|
||||
//
|
||||
template<uint8_t Byte_Count__>
|
||||
struct _Bitfield {
|
||||
|
||||
uint8_t byte_;
|
||||
|
||||
_Bitfield < Byte_Count__ - 1 > more_bytes_;
|
||||
|
||||
// The constructor initializes the first eight bits that are stored
|
||||
// in this struct and passes the remaining bits on to the nested
|
||||
// more_bytes_ struct.
|
||||
//
|
||||
template<typename ... Bits__>
|
||||
constexpr _Bitfield(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3,
|
||||
uint8_t b4, uint8_t b5, uint8_t b6, uint8_t b7,
|
||||
Bits__ ... bits)
|
||||
: byte_(b0 << 0
|
||||
| b1 << 1
|
||||
| b2 << 2
|
||||
| b3 << 3
|
||||
| b4 << 4
|
||||
| b5 << 5
|
||||
| b6 << 6
|
||||
| b7 << 7),
|
||||
more_bytes_(bits...)
|
||||
{}
|
||||
|
||||
} __attribute__((packed));
|
||||
|
||||
// A specialization that stores any remaining 1 to 8 bits. Any bits that
|
||||
// are unused are zeroed by default.
|
||||
//
|
||||
template<>
|
||||
struct _Bitfield<1 /* the last byte */> {
|
||||
|
||||
uint8_t byte_;
|
||||
|
||||
constexpr _Bitfield(uint8_t b0 = 0, uint8_t b1 = 0, uint8_t b2 = 0, uint8_t b3 = 0,
|
||||
uint8_t b4 = 0, uint8_t b5 = 0, uint8_t b6 = 0, uint8_t b7 = 0)
|
||||
: byte_(b0 << 0
|
||||
| b1 << 1
|
||||
| b2 << 2
|
||||
| b3 << 3
|
||||
| b4 << 4
|
||||
| b5 << 5
|
||||
| b6 << 6
|
||||
| b7 << 7)
|
||||
{}
|
||||
} __attribute__((packed));
|
||||
|
||||
// A common base class that provides methods to access the bits in
|
||||
// a _Bitfield instance by treating it as a raw array.
|
||||
//
|
||||
class _BaseBitfield {
|
||||
|
||||
protected:
|
||||
|
||||
static bool isBitSetP(const void *bit_field, uint8_t raw_pos);
|
||||
static void setBitP(void *bit_field, uint8_t raw_pos, uint8_t val);
|
||||
static bool isBitSetPROGMEM_P(const void *bit_field, uint8_t raw_pos);
|
||||
};
|
||||
|
||||
} // end namespace internal
|
||||
|
||||
// This is the actual user class.
|
||||
//
|
||||
template<size_t BitCount__>
|
||||
class Bitfield : public internal::_BaseBitfield {
|
||||
|
||||
public:
|
||||
|
||||
static constexpr size_t nBytesForBits(size_t n_bits) {
|
||||
return (n_bits % 8) ? n_bits / 8 + 1 : n_bits / 8;
|
||||
}
|
||||
|
||||
static constexpr size_t n_bits_ = BitCount__;
|
||||
static constexpr size_t n_bytes_ = nBytesForBits(BitCount__);
|
||||
|
||||
template<typename ... Bits__>
|
||||
constexpr Bitfield(Bits__...bits) : bits_(bits...) {
|
||||
static_assert(sizeof...(Bits__) == n_bits_,
|
||||
"Invalid number of bits supplied to Bitfield<BitCount__> constructor. \n"
|
||||
"Compare the number of bits supplied with the provided template \n"
|
||||
"parameter BitCount__ to find out what went wrong.\n");
|
||||
}
|
||||
|
||||
bool isBitSet(uint8_t raw_pos) const {
|
||||
return isBitSetP(&bits_, raw_pos);
|
||||
}
|
||||
|
||||
bool isBitSetPROGMEM(uint8_t raw_pos) const {
|
||||
return isBitSetPROGMEM_P(&bits_, raw_pos);
|
||||
}
|
||||
|
||||
void setBit(uint8_t raw_pos, uint8_t val) {
|
||||
setBitP(&bits_, raw_pos, val);
|
||||
}
|
||||
|
||||
// An operator to query bits.
|
||||
// Note: This assumes bitfield to be stored in PROGMEM. Use the
|
||||
// access method isBitSet(raw_pos) otherwise.
|
||||
//
|
||||
constexpr bool operator[](uint8_t raw_pos) const {
|
||||
return this->isBitSetPROGMEM_P(raw_pos);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
internal::_Bitfield<n_bytes_> bits_;
|
||||
};
|
||||
|
||||
// The method generateBitfield may be used to conveniently create bitfields.
|
||||
//
|
||||
// auto my_bitfield = generateBitfield(1, 2, 3);
|
||||
//
|
||||
// Note: Due to a restriction in all gcc versions < 6.0
|
||||
// this function cannot be called with the output of KEYMAP_STACKED(...)
|
||||
// or KEYMAP(...) as its arguments.
|
||||
//
|
||||
template<typename...Bits__>
|
||||
constexpr Bitfield<sizeof...(Bits__)> generateBitfield(Bits__...bits) {
|
||||
return Bitfield<sizeof...(Bits__)>(bits...);
|
||||
}
|
||||
|
||||
// This auxiliary method determines the number of entries in a list.
|
||||
//
|
||||
namespace internal {
|
||||
|
||||
template<typename...Args__>
|
||||
constexpr size_t nListEntries(Args__...) {
|
||||
return sizeof...(Args__);
|
||||
}
|
||||
|
||||
} // end namespace internal
|
||||
|
||||
// Defines a bitfield named VAR_NAME. Applies MODIFIER (could e.g. set to const PROGMEM).
|
||||
//
|
||||
#define BITFIELD__(VAR_NAME, MODIFIER, ...) \
|
||||
constexpr size_t VAR_NAME##_n_bits __NL__ \
|
||||
= kaleidoscope::bitfields::internal::nListEntries(__VA_ARGS__);__NL__ \
|
||||
__NL__ \
|
||||
MODIFIER kaleidoscope::bitfields::Bitfield<VAR_NAME##_n_bits> __NL__ \
|
||||
VAR_NAME{__VA_ARGS__};
|
||||
|
||||
#define BITFIELD(VAR_NAME, ...) BITFIELD__(VAR_NAME,, __VA_ARGS__)
|
||||
#define BITFIELD_PROGMEM(VAR_NAME, ...) BITFIELD__(VAR_NAME, const PROGMEM, __VA_ARGS__)
|
||||
|
||||
#ifdef KEYMAP_GENERIC
|
||||
#define KEYMAP_BITFIELD(VAR_NAME, ...) \
|
||||
BITFIELD(VAR_NAME, KEYMAP_GENERIC(0 /*default for non-existent keys*/, __VA_ARGS__))
|
||||
|
||||
#define KEYMAP_BITFIELD_PROGMEM(VAR_NAME, ...) \
|
||||
BITFIELD_PROGMEM(VAR_NAME, KEYMAP_GENERIC(0 /*default for non-existent keys*/, __VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifdef KEYMAP_STACKED_GENERIC
|
||||
#define KEYMAP_BITFIELDK_STACKED(VAR_NAME, ...) \
|
||||
BITFIELD(VAR_NAME, KEYMAP_STACKED_GENERIC(0 /*default for non-existent keys*/, __VA_ARGS__))
|
||||
|
||||
#define KEYMAP_BITFIELD_STACKED_PROGMEM(VAR_NAME, ...) \
|
||||
BITFIELD_PROGMEM(VAR_NAME, KEYMAP_STACKED_GENERIC(0 /*default for non-existent keys*/, __VA_ARGS__))
|
||||
#endif
|
||||
|
||||
} // end namespace bitfields
|
||||
} // end namespace kaleidoscope
|
Loading…
Reference in new issue