mirror of
https://github.com/RetroDECK/Supermodel.git
synced 2025-03-06 14:27:44 +00:00
349 lines
8.6 KiB
C++
349 lines
8.6 KiB
C++
#include "Util/BitRegister.h"
|
|
#include <cstring>
|
|
#include <cctype>
|
|
#include <string>
|
|
|
|
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;
|
|
}
|
|
|
|
uint8_t BitRegister::GetBit(size_t pos) const
|
|
{
|
|
if (pos < Size())
|
|
return m_bits[pos];
|
|
return m_no_data;
|
|
}
|
|
|
|
// Convert entire bit vector into a 64-bit integer. If bit register is larger
|
|
// than 64 bits, only least significant bits will be present.
|
|
uint64_t BitRegister::GetBits() const
|
|
{
|
|
return GetBits(0, Size());
|
|
}
|
|
|
|
// Convert a subset of the bit vector into a 64-bit integer. If the data does
|
|
// not fit into 64 bits, only the least significant (rightmost) bits. Result
|
|
// undefined if range falls outside of vector.
|
|
uint64_t BitRegister::GetBits(size_t from, size_t count) const
|
|
{
|
|
uint64_t value = 0;
|
|
if (from + count > Size())
|
|
return value;
|
|
for (size_t i = from; i < from + count; i++)
|
|
{
|
|
value <<= 1;
|
|
value |= GetBit(i);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
// Shift a bit into the right side, growing the vector by 1
|
|
void BitRegister::AddToRight(uint8_t bit)
|
|
{
|
|
m_bits.push_back(!!bit);
|
|
}
|
|
|
|
// Shift a bit into the left side, growing the vector by 1
|
|
void BitRegister::AddToLeft(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::RemoveFromLeft()
|
|
{
|
|
uint8_t ejected = GetLeftmost();
|
|
RemoveFromLeft(1);
|
|
return ejected;
|
|
}
|
|
|
|
// Shift left and lose bits (shrinks vector)
|
|
void BitRegister::RemoveFromLeft(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::RemoveFromRight()
|
|
{
|
|
uint8_t ejected = GetRightmost();
|
|
RemoveFromRight(1);
|
|
return ejected;
|
|
}
|
|
|
|
// Shift right and lose bits (shrinks vector)
|
|
void BitRegister::RemoveFromRight(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 Rightmost bits, shifting in new bits from left to
|
|
// preserve vector size
|
|
void BitRegister::ShiftRight(size_t count)
|
|
{
|
|
if (Empty())
|
|
return;
|
|
|
|
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 (Empty())
|
|
return;
|
|
|
|
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);
|
|
}
|
|
|
|
// Shift right and eject Rightmost bit, shifting new bit into left side to
|
|
// preserve vector size
|
|
uint8_t BitRegister::ShiftOutRight(uint8_t bit)
|
|
{
|
|
if (Empty())
|
|
return m_no_data;
|
|
|
|
uint8_t ejected = GetRightmost();
|
|
ShiftRight(1);
|
|
m_bits[0] = !!bit;
|
|
return ejected;
|
|
}
|
|
|
|
// Shift left and eject left-most bit, shifting new bit into right side to
|
|
// preserve vector size
|
|
uint8_t BitRegister::ShiftOutLeft(uint8_t bit)
|
|
{
|
|
if (Empty())
|
|
return m_no_data;
|
|
|
|
uint8_t ejected = GetLeftmost();
|
|
ShiftLeft(1);
|
|
m_bits[m_bits.size() - 1] = !!bit;
|
|
return ejected;
|
|
}
|
|
|
|
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()
|
|
{
|
|
if (Empty())
|
|
return;
|
|
memset(m_bits.data(), 0, m_bits.size());
|
|
}
|
|
|
|
void BitRegister::SetZeros(size_t count)
|
|
{
|
|
m_bits.resize(count);
|
|
memset(m_bits.data(), 0, count);
|
|
}
|
|
|
|
void BitRegister::SetOnes()
|
|
{
|
|
if (Empty())
|
|
return;
|
|
memset(m_bits.data(), 1, m_bits.size());
|
|
}
|
|
|
|
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::string BitRegister::ToHexString() const
|
|
{
|
|
static const char digits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
|
|
if (Empty())
|
|
return std::string();
|
|
size_t partial = Size() & 3;
|
|
size_t num_digits = Size() / 4 + (partial != 0 ? 1 : 0);
|
|
std::string out(num_digits + 2, '0');
|
|
out[0] = '0';
|
|
out[1] = 'x';
|
|
size_t idx = 0;
|
|
size_t digit_idx = 2;
|
|
size_t digit = 0;
|
|
if (partial != 0)
|
|
{
|
|
for (idx = 0; idx < partial; idx++)
|
|
{
|
|
digit <<= 1;
|
|
digit |= m_bits[idx];
|
|
}
|
|
out[digit_idx++] = digits[digit];
|
|
}
|
|
while (idx < Size())
|
|
{
|
|
digit = m_bits[idx++] << 3;
|
|
digit |= m_bits[idx++] << 2;
|
|
digit |= m_bits[idx++] << 1;
|
|
digit |= m_bits[idx++] << 0;
|
|
out[digit_idx++] = digits[digit];
|
|
}
|
|
return out;
|
|
}
|
|
|
|
std::ostream &operator<<(std::ostream &os, const BitRegister ®)
|
|
{
|
|
if (reg.Empty())
|
|
{
|
|
os << "[ empty ]";
|
|
return os;
|
|
}
|
|
os << "[ " << reg.Size() << ": " << reg.ToHexString() << " ]";
|
|
return os;
|
|
}
|
|
|
|
BitRegister::BitRegister()
|
|
{
|
|
}
|
|
|
|
BitRegister::BitRegister(size_t count)
|
|
{
|
|
SetZeros(count);
|
|
}
|
|
|
|
BitRegister::BitRegister(size_t count, uint8_t bit)
|
|
{
|
|
if (bit == 0)
|
|
SetZeros(count);
|
|
else
|
|
SetOnes(count);
|
|
}
|
|
} // Util
|