diff --git a/Src/Util/BitRegister.cpp b/Src/Util/BitRegister.cpp new file mode 100644 index 0000000..9695b07 --- /dev/null +++ b/Src/Util/BitRegister.cpp @@ -0,0 +1,228 @@ +#include "Util/BitRegister.h" +#include +#include + +namespace Util +{ + uint8_t BitRegister::GetLeftMost() const + { + return m_bits.empty() ? m_no_data : m_bits.front(); + } + + uint8_t BitRegister::GetRightMost() const + { + return m_bits.empty() ? m_no_data : m_bits.back(); + } + + size_t BitRegister::HexStart(const std::string &value) + { + if (value.length() > 1 && value[0] == '$') + return 1; + if (value.length() > 2 && value[0] == '0' && (value[1] == 'x' || value[1] == 'X')) + return 2; + return std::string::npos; + } + + size_t BitRegister::BinStart(const std::string &value) + { + if (value.length() > 1 && value[0] == '%') + return 1; + if (value.length() > 2 && value[0] == '0' && (value[1] == 'b' || value[1] == 'B')) + return 2; + return 0; + } + + size_t BitRegister::CountBitsHex(const std::string &value, size_t startPos) + { + return 4 * (value.length() - startPos); + } + + size_t BitRegister::CountBitsBin(const std::string &value, size_t startPos) + { + return value.length() - startPos; + } + + // Shift a bit into the right side, growing the vector by 1 + void BitRegister::ShiftInRight(uint8_t bit) + { + m_bits.push_back(!!bit); + } + + // Shift a bit into the left side, growing the vector by 1 + void BitRegister::ShiftInLeft(uint8_t bit) + { + m_bits.push_back(0); + ShiftRight(1); + m_bits[0] = !!bit; + } + + // Shift left by 1, returning ejected bit (shrinks vector) + uint8_t BitRegister::ShiftOutLeft() + { + uint8_t ejected = GetLeftMost(); + ShiftOutLeft(1); + return ejected; + } + + // Shift left and lose bits (shrinks vector) + void BitRegister::ShiftOutLeft(size_t count) + { + if (count >= m_bits.size()) + { + // All bits shifted out + m_bits.clear(); + return; + } + + // Shift and resize + memmove(m_bits.data(), m_bits.data() + count, m_bits.size() - count); + m_bits.resize(m_bits.size() - count); + } + + // Shift right by 1, returning ejected bit (shrinks vector) + uint8_t BitRegister::ShiftOutRight() + { + uint8_t ejected = GetRightMost(); + ShiftOutRight(1); + return ejected; + } + + // Shift right and lose bits (shrinks vector) + void BitRegister::ShiftOutRight(size_t count) + { + // Shifting right means we lose lower bits (which are higher in the + // vector), which means we just trim the vector from the right side + if (count >= m_bits.size()) + { + m_bits.clear(); + return; + } + m_bits.resize(m_bits.size() - count); + } + + // Shift right and lose right-most bits, shifting in new bits from left to + // preserve vector size + void BitRegister::ShiftRight(size_t count) + { + if (count < m_bits.size()) + { + // Shift over bits to the right + memmove(m_bits.data() + count, m_bits.data(), m_bits.size() - count); + } + + // Fill in the left with "no data" + memset(m_bits.data(), m_no_data, count); + } + + // Shift left and lose left-most bits, shifting in new bits from right to + // preserve vector size + void BitRegister::ShiftLeft(size_t count) + { + if (count < m_bits.size()) + { + // Shift over bits to the left + memmove(m_bits.data(), m_bits.data() + count, m_bits.size() - count); + } + + // Fill in the right with "no data" + memset(m_bits.data() + m_bits.size() - count, m_no_data, count); + } + + void BitRegister::Reset() + { + m_bits.clear(); + } + + // Set single bit, indexed from left, without expanding vector + void BitRegister::SetBit(size_t bitPos, uint8_t value) + { + if (bitPos < m_bits.size()) + m_bits[bitPos] = !!value; + } + + // Insert value, indexed from left, without expanding vector + void BitRegister::Insert(size_t bitPos, const std::string &value) + { + size_t hexStart = HexStart(value); + if (hexStart != std::string::npos) + { + for (size_t i = hexStart; i < value.length(); i++) + { + char digit = tolower(value[i]); + uint8_t nibble = 0; + if (isxdigit(digit)) + nibble = (digit >= 'a') ? (digit - 'a' + 10) : (digit - '0'); + SetBit(bitPos++, nibble & 8); + SetBit(bitPos++, nibble & 4); + SetBit(bitPos++, nibble & 2); + SetBit(bitPos++, nibble & 1); + } + } + else + { + for (size_t i = BinStart(value); i < value.length(); i++) + { + SetBit(bitPos++, value[i] == '0' ? 0 : 1); + } + } + } + + void BitRegister::Set(const std::string &value) + { + size_t hexStart = HexStart(value); + size_t binStart = BinStart(value); + if (hexStart != std::string::npos) + m_bits.resize(CountBitsHex(value, hexStart)); + else + m_bits.resize(CountBitsBin(value, binStart)); + Insert(0, value); + } + + void BitRegister::SetZeros(size_t count) + { + m_bits.resize(count); + memset(m_bits.data(), 0, count); + } + + void BitRegister::SetOnes(size_t count) + { + m_bits.resize(count); + memset(m_bits.data(), 1, count); + } + + + void BitRegister::SetNoBitValue(uint8_t bit) + { + m_no_data = bit == 0 ? 0 : 1; + } + + std::string BitRegister::ToBinaryString() const + { + if (Empty()) + return std::string(""); + std::string out(Size(), '0'); + for (size_t i = 0; i < Size(); i++) + { + out[i] = m_bits[i] == 0 ? '0' : '1'; + } + return out; + } + + std::ostream &operator<<(std::ostream &os, const BitRegister ®) + { + if (reg.Empty()) + { + os << "[ empty ]"; + return os; + } + char *buf = new char[reg.Size() + 1]; + buf[reg.Size()] = 0; + for (size_t i = 0; i < reg.Size(); i++) + { + buf[i] = reg.m_bits[i] == 0 ? '0' : '1'; + } + os << "[ " << reg.Size() << ": " << buf << " ]"; + delete [] buf; + return os; + } +} // Util diff --git a/Src/Util/BitRegister.h b/Src/Util/BitRegister.h new file mode 100644 index 0000000..9f22bb5 --- /dev/null +++ b/Src/Util/BitRegister.h @@ -0,0 +1,77 @@ +#ifndef INCLUDED_UTIL_BITREGISTER_H +#define INCLUDED_UTIL_BITREGISTER_H + +#include +#include +#include + +namespace Util +{ + class BitRegister + { + public: + inline bool Empty() const + { + return m_bits.empty(); + } + + inline size_t Size() const + { + return m_bits.size(); + } + + // Functions that grow/shrink the bit register + void ShiftInRight(uint8_t bit); + void ShiftInLeft(uint8_t bit); + uint8_t ShiftOutLeft(); + void ShiftOutLeft(size_t count); + uint8_t ShiftOutRight(); + void ShiftOutRight(size_t count); + + // Functions that preserve the current register size, shifting in the "no + // data" value as needed + void ShiftRight(size_t count); + void ShiftLeft(size_t count); + + // Functions that insert bits, clipped against current register size + void SetBit(size_t bitPos, uint8_t value); + void Insert(size_t bitPos, const std::string &value); + + // Functions that reset the contents (and size) of the register + void Set(const std::string &value); + void SetZeros(size_t count); + void SetOnes(size_t count); + void SetNoBitValue(uint8_t bit); + void Reset(); + + // String serialization + std::string ToBinaryString() const; + friend std::ostream &operator<<(std::ostream &os, const BitRegister ®); + + private: + /* + * Vector layout: + * + * Index: 0 1 2 3 ... N + * +---+---+---+---+-...-+---+ + * <-- left -- | 1 | 0 | 1 | 1 | ... | 1 | -- right --> + * +---+---+---+---+-...-+---+ + * + * "Left" means lower indices in the array. To remain flexible and agnostic + * about MSB vs. LSB and bit numbers, all functions are explicit about + * whether they are shifting in/out of the left/right side. It is up to the + * user to establish a consistent convention according to their use case. + */ + std::vector m_bits; + uint8_t m_no_data = 0; // by default, assume non-existent bits are 0 + + uint8_t GetLeftMost() const; + uint8_t GetRightMost() const; + static size_t HexStart(const std::string &value); + static size_t BinStart(const std::string &value); + static size_t CountBitsHex(const std::string &value, size_t startPos); + static size_t CountBitsBin(const std::string &value, size_t startPos); + }; +} // Util + +#endif // INCLUDED_UTIL_BITREGISTER_H diff --git a/Src/Util/Test_BitRegister.cpp b/Src/Util/Test_BitRegister.cpp new file mode 100644 index 0000000..7806463 --- /dev/null +++ b/Src/Util/Test_BitRegister.cpp @@ -0,0 +1,227 @@ +#include "Util/BitRegister.h" +#include + +int main(int argc, char **argv) +{ + std::vector expected; + std::vector results; + + Util::BitRegister reg; + + + // Test + reg.SetZeros(1); + expected.push_back("0"); + results.push_back(reg.ToBinaryString()); + + // Test + reg.SetZeros(2); + expected.push_back("00"); + results.push_back(reg.ToBinaryString()); + + // Test + reg.SetZeros(3); + expected.push_back("000"); + results.push_back(reg.ToBinaryString()); + + // Test + reg.SetOnes(1); + expected.push_back("1"); + results.push_back(reg.ToBinaryString()); + + // Test + reg.SetOnes(2); + expected.push_back("11"); + results.push_back(reg.ToBinaryString()); + + // Test + reg.SetOnes(3); + expected.push_back("111"); + results.push_back(reg.ToBinaryString()); + + // Test + reg.Reset(); + expected.push_back(""); + results.push_back(reg.ToBinaryString()); + + // Test + reg.Reset(); + reg.Set("101000110101111100101"); + expected.push_back("101000110101111100101"); + results.push_back(reg.ToBinaryString()); + + // Test + reg.Reset(); + reg.Set("%101000110101111100101"); + expected.push_back("101000110101111100101"); + results.push_back(reg.ToBinaryString()); + + // Test + reg.Reset(); + reg.Set("0b101000110101111100101"); + expected.push_back("101000110101111100101"); + results.push_back(reg.ToBinaryString()); + + // Test + reg.Reset(); + reg.Set("0B101000110101111100101"); + expected.push_back("101000110101111100101"); + results.push_back(reg.ToBinaryString()); + + // Test + reg.Reset(); + reg.Set("$1d"); + expected.push_back("00011101"); + results.push_back(reg.ToBinaryString()); + + // Test + reg.Reset(); + reg.Set("0x1d"); + expected.push_back("00011101"); + results.push_back(reg.ToBinaryString()); + + // Test + reg.Reset(); + reg.Set("0X1d"); + expected.push_back("00011101"); + results.push_back(reg.ToBinaryString()); + + // Test + reg.Reset(); + reg.Set("110010101"); + expected.push_back("1"); + results.push_back(reg.ShiftOutRight() == 1 ? "1" : "0"); + expected.push_back("11001010"); + results.push_back(reg.ToBinaryString()); + + expected.push_back("0"); + results.push_back(reg.ShiftOutRight() == 1 ? "1" : "0"); + expected.push_back("1100101"); + results.push_back(reg.ToBinaryString()); + + expected.push_back("1"); + results.push_back(reg.ShiftOutRight() == 1 ? "1" : "0"); + expected.push_back("110010"); + results.push_back(reg.ToBinaryString()); + + // Test + reg.Reset(); + reg.Set("110010101"); + expected.push_back("1"); + results.push_back(reg.ShiftOutLeft() == 1 ? "1" : "0"); + expected.push_back("10010101"); + results.push_back(reg.ToBinaryString()); + + expected.push_back("1"); + results.push_back(reg.ShiftOutLeft() == 1 ? "1" : "0"); + expected.push_back("0010101"); + results.push_back(reg.ToBinaryString()); + + expected.push_back("0"); + results.push_back(reg.ShiftOutLeft() == 1 ? "1" : "0"); + expected.push_back("010101"); + results.push_back(reg.ToBinaryString()); + + // Test + reg.Reset(); + reg.Set("0x12345"); + reg.ShiftOutLeft(1); + expected.push_back("0010010001101000101"); + results.push_back(reg.ToBinaryString()); + + reg.ShiftOutLeft(2); + expected.push_back("10010001101000101"); + results.push_back(reg.ToBinaryString()); + + // Test + reg.Reset(); + reg.Set("0x12345"); + reg.ShiftLeft(1); + expected.push_back("00100100011010001010"); + results.push_back(reg.ToBinaryString()); + + reg.ShiftLeft(2); + expected.push_back("10010001101000101000"); + results.push_back(reg.ToBinaryString()); + + // Test + reg.Reset(); + reg.Set("0x12345"); + reg.ShiftOutRight(1); + expected.push_back("0001001000110100010"); + results.push_back(reg.ToBinaryString()); + + reg.ShiftOutRight(2); + expected.push_back("00010010001101000"); + results.push_back(reg.ToBinaryString()); + + // Test + reg.Reset(); + reg.Set("0x12345"); + reg.ShiftRight(1); + expected.push_back("00001001000110100010"); + results.push_back(reg.ToBinaryString()); + + reg.ShiftRight(2); + expected.push_back("00000010010001101000"); + results.push_back(reg.ToBinaryString()); + + // Test + reg.Reset(); + reg.ShiftInRight(1); + reg.ShiftInRight(1); + reg.ShiftInRight(0); + reg.ShiftInRight(1); + reg.ShiftInRight(0); + expected.push_back("11010"); + results.push_back(reg.ToBinaryString()); + + // Test + reg.Reset(); + reg.ShiftInRight(1); + reg.ShiftInRight(1); + reg.ShiftInRight(0); + reg.ShiftInRight(1); + reg.ShiftInRight(0); + reg.ShiftOutRight(); + expected.push_back("1101"); + results.push_back(reg.ToBinaryString()); + + reg.ShiftOutLeft(); + expected.push_back("101"); + results.push_back(reg.ToBinaryString()); + + // Test + reg.Reset(); + reg.SetZeros(32); + reg.Insert(31, "0xffff"); + expected.push_back("00000000000000000000000000000001"); + results.push_back(reg.ToBinaryString()); + + reg.Insert(16, "$1234"); + expected.push_back("00000000000000000001001000110100"); + results.push_back(reg.ToBinaryString()); + + reg.Insert(14, "101"); + expected.push_back("00000000000000101001001000110100"); + results.push_back(reg.ToBinaryString()); + + reg.Insert(29, "0110"); + expected.push_back("00000000000000101001001000110011"); + results.push_back(reg.ToBinaryString()); + + // Check results + size_t num_failed = 0; + for (size_t i = 0; i < expected.size(); i++) + { + if (expected[i] != results[i]) + { + std::cout << "Test #" << i << " FAILED. Expected \"" << expected[i] << "\" but got \"" << results[i] << '\"' << std::endl; + num_failed++; + } + } + + if (num_failed == 0) + std::cout << "All tests passed!" << std::endl; + return 0; +} \ No newline at end of file