mirror of
https://github.com/RetroDECK/Supermodel.git
synced 2024-11-22 13:55:38 +00:00
Added BitRegister class (for use in upcoming JTAG refactor): an inefficient but flexible container for manipulating a dynamic bit stream.
This commit is contained in:
parent
a3e2fb7906
commit
f2030ac125
228
Src/Util/BitRegister.cpp
Normal file
228
Src/Util/BitRegister.cpp
Normal file
|
@ -0,0 +1,228 @@
|
|||
#include "Util/BitRegister.h"
|
||||
#include <cstring>
|
||||
#include <cctype>
|
||||
|
||||
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
|
77
Src/Util/BitRegister.h
Normal file
77
Src/Util/BitRegister.h
Normal file
|
@ -0,0 +1,77 @@
|
|||
#ifndef INCLUDED_UTIL_BITREGISTER_H
|
||||
#define INCLUDED_UTIL_BITREGISTER_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <ostream>
|
||||
|
||||
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<uint8_t> 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
|
227
Src/Util/Test_BitRegister.cpp
Normal file
227
Src/Util/Test_BitRegister.cpp
Normal file
|
@ -0,0 +1,227 @@
|
|||
#include "Util/BitRegister.h"
|
||||
#include <iostream>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
std::vector<std::string> expected;
|
||||
std::vector<std::string> 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;
|
||||
}
|
Loading…
Reference in a new issue