From f764a78ecd419dac7a473a64d71d4b53170f6679 Mon Sep 17 00:00:00 2001 From: Florian Fleissner Date: Thu, 9 May 2019 11:08:16 -0700 Subject: [PATCH] New classes/macros: * 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 --- src/kaleidoscope/bitfields.cpp | 53 +++++++++ src/kaleidoscope/bitfields.h | 200 +++++++++++++++++++++++++++++++++ 2 files changed, 253 insertions(+) create mode 100644 src/kaleidoscope/bitfields.cpp create mode 100644 src/kaleidoscope/bitfields.h diff --git a/src/kaleidoscope/bitfields.cpp b/src/kaleidoscope/bitfields.cpp new file mode 100644 index 00000000..23bb055b --- /dev/null +++ b/src/kaleidoscope/bitfields.cpp @@ -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 . + */ + +#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(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(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(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 diff --git a/src/kaleidoscope/bitfields.h b/src/kaleidoscope/bitfields.h new file mode 100644 index 00000000..22e0e4e1 --- /dev/null +++ b/src/kaleidoscope/bitfields.h @@ -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 . + */ + +#pragma once + +#include + +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 +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 + 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 +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 + constexpr Bitfield(Bits__...bits) : bits_(bits...) { + static_assert(sizeof...(Bits__) == n_bits_, + "Invalid number of bits supplied to Bitfield 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 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 +constexpr Bitfield generateBitfield(Bits__...bits) { + return Bitfield(bits...); +} + +// This auxiliary method determines the number of entries in a list. +// +namespace internal { + +template +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 __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