diff --git a/src/kaleidoscope/MatrixAddr.h b/src/kaleidoscope/MatrixAddr.h new file mode 100644 index 00000000..61aa9951 --- /dev/null +++ b/src/kaleidoscope/MatrixAddr.h @@ -0,0 +1,255 @@ +/* Kaleidoscope - Firmware for computer input devices + * Copyright (C) 2013-2019 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 +#include + +namespace kaleidoscope { + +template +class MatrixAddr { + private: + + uint8_t offset_; + + public: + + typedef MatrixAddr ThisType; + + static constexpr uint8_t rows = rows__; + static constexpr uint8_t cols = cols__; + static constexpr uint8_t upper_limit = rows__ * cols__; + + static constexpr uint8_t invalid_state = 255; + + static_assert(rows * cols < 255, + "The number of rows and columns provided to instanciate \n" + "MatrixAddr exceeds the supported total number \n" + "of 255 keys"); + + constexpr MatrixAddr() : offset_(invalid_state) {} + + constexpr MatrixAddr(uint8_t row, uint8_t col) + : offset_(row * cols + col) {} + + constexpr MatrixAddr(uint8_t offset) + : offset_(offset) {} + + // Rely on the default copy and move constructor. + // + // Note: If these were implemented naively as the commented versions below, + // some versions of avr-gcc (e.g. 4.9.2 or 5.4 would generate + // ridiculously bad assembler code for each copy construction, + // that would bloat the default firmware by 1K of PROGMEM! + // + constexpr MatrixAddr(const ThisType &other) = default; + constexpr MatrixAddr(ThisType &&other) = default; + //constexpr MatrixAddr(const ThisType &other) : offset_(other.offset_) {} + //constexpr MatrixAddr(ThisType &&other) : offset_(other.offset_) {} + + ThisType &operator=(const ThisType &) = default; + ThisType &operator=(ThisType &&) = default; + + template + constexpr MatrixAddr(const MatrixAddr__ &other) + : MatrixAddr(other.row(), other.col()) {} + + constexpr uint8_t row() const { + return offset_ / cols; + } + constexpr uint8_t col() const { + return offset_ % cols; + } + + void setRow(uint8_t r) { + //assert(r < rows); + offset_ = this->col() + r * cols; + } + void setCol(uint8_t c) { + //assert(c < cols); + offset_ = this->row() * cols + c; + } + + void shift(int8_t rows, int8_t cols) { + offset_ += rows * cols + cols; + } + + void rowShift(int8_t rows) { + offset_ += rows * cols; + } + + void colShift(int8_t cols) { + offset_ += cols; + } + + constexpr ThisType shifted(int8_t row_offset, int8_t col_offset) const { + return ThisType(uint8_t(offset_ + row_offset * cols + col_offset)); + } + + constexpr ThisType rowShifted(int8_t row_offset) const { + return ThisType(uint8_t(offset_ + row_offset * cols)); + } + + constexpr ThisType colShifted(int8_t col_offset) const { + return ThisType(uint8_t(offset_ + col_offset)); + } + + constexpr uint8_t toInt() const { + return offset_; + } + + constexpr bool isValid() const { + return offset_ < upper_limit; + } + + ThisType operator++() { + ++offset_; + return *this; + } + + ThisType operator++(int) { // postfix ++ + ThisType copy(*this); + ++*this; // call the prefix increment + return copy; + } + + ThisType operator--() { + --offset_; + return *this; + } + + ThisType operator--(int) { // postfix ++ + ThisType copy(*this); + --*this; // call the prefix increment + return copy; + } + + template + ThisType &operator+(const MatrixAddr__ & other) { + *this = ThisType(this->row() + other.row(), + this->col() + other.col()); + return *this; + } + + template + ThisType &operator-(const MatrixAddr__ & other) { + *this = ThisType(this->row() - other.row(), + this->col() - other.col()); + return *this; + } + + template + ThisType &operator+=(const MatrixAddr__ & other) { + *this = *this + other; + return *this; + } + + template + ThisType &operator-=(const MatrixAddr__ & other) { + *this = *this - other; + return *this; + } + + bool operator==(const ThisType &other) const { + return offset_ == other.offset_; + } + + bool operator!=(const ThisType &other) const { + return offset_ != other.offset_; + } + + // Range is a helper class that is used in range based for loops + // over all possible KeyAddr values. + // + struct Range { + typedef ThisType Iterator; + static constexpr ThisType begin() { + return ThisType(uint8_t(0)); + } + static constexpr ThisType end() { + return ThisType(ThisType::upper_limit); + } + }; + + // Use this method in range based for loops as e.g. + // for(auto key_addr: KeyAddr::all()) { ... } + // + static constexpr Range all() { + return Range{}; + } + + // This operator is needed to let MatrixAddr serve as Iterator type + // to iterate over a Range. + // + constexpr const ThisType &operator*() const { + return *this; + } +}; + +// Comparison operators only used for module testing. This is because they +// cause ambiguous symbol lookup when used in the regular firmware. +// +// To use them also for the regular firmware they would need to be +// disambiguated by moving them to the Kaleidoscope.h header and replacing +// them with non-template versions that operate on the actual typedefed +// KeyAddr and KeyAddr. + +#ifdef MATRIX_ADDR_TESTING + +template +bool operator==(const MatrixAddr1__ & a1, const MatrixAddr2__ & a2) { + return (a1.row() == a2.row()) && (a1.col() == a2.col()); +} +template +bool operator!=(const MatrixAddr1__ & a1, const MatrixAddr2__ & a2) { + return !operator==(a1, a2); +} + +template +bool operator>(const MatrixAddr1__ & a1, const MatrixAddr2__ & a2) { + return (a1.row() > a2.row()) + || ((a1.row() == a2.row()) && (a1.col() > a2.col())); +} + +template +bool operator<(const MatrixAddr1__ & a1, const MatrixAddr2__ & a2) { + // This could be optimized if necessary + return !operator>(a1, a2) && !operator==(a1, a2); +} + +template +bool operator>=(const MatrixAddr1__ & a1, const MatrixAddr2__ & a2) { + // This could be optimized if necessary + return operator>(a1, a2) || operator==(a1, a2); +} + +template +bool operator<=(const MatrixAddr1__ & a1, const MatrixAddr2__ & a2) { + return !operator>(a1, a2); +} + +#endif + +} // end namespace kaleidoscope + +// Row/col based access functions have been superseded by matrix address +// base access. +// +#define _DEPRECATED_MESSAGE_ROW_COL_FUNC \ + "Row/col based access functions have been deprecated. Please use " \ + "the KeyAddr/KeyAddr based versions instead."