Как заменить биты в битовом поле, не влияя на другие биты, используя c

Я хотел заменить бит/биты (более 1) в 32/64 бит данных, не затрагивая другие биты. Например, например:

У меня есть 64-битный регистр, где биты 5 и 6 могут принимать значения 0,1,2,3.

5:6    
0 0
0 1
1 0
1 1     

Теперь, когда я читаю регистр, я получаю значение слова 0x146 (0001 0 10 0 0110). Теперь я хочу изменить значение в позиции бит 5 и 6 на 01. (сейчас это 10, которое равно 2 в десятичной и я хочу заменить его на 1 e 01), если другие биты не будут затронуты, и запишите регистр только с измененными битами 5 и 6 (поэтому после изменения становится 126)

Я пробовал делать это

reg_data=0x146
reg_data |= 1 << shift   (shift in this case is 5)

Если я делаю это значение в позициях бит 5 & 6 станет 11 (0x3), а не 01 (0x1), который я хотел.

  • Как мне делать чтение/изменение/запись?
  • Как заменить только определенные бит/биты в 32-битных полях, не затрагивая все данные поля, используя C?

Настройка бит в порядке, но более одного бита, мне сложно это сделать.

Любые предложения приветствуются.

Ответ 1

Почему бы вам не использовать битовую маску? Пример:

new_value = 0, 1, 2 or 3  // (this is the value you will set in)
bit_mask = (3<<5)         // (mask of the bits you want to set)
reg_data = (reg_data & (~bit_mask)) | (new_value<<5)

Это сохраняет старые биты и OR в новых.

Ответ 2

reg_data &= ~( (1 << shift1) | (1 << shift2) );
reg_data |= ( (1 << shift1) | (1 << shift2) );

Первая строка очищает два бита в (shift1, shift2), а вторая строка устанавливает их.

Ответ 3

Вот общий процесс, который действует на длинный массив, считая его длинным битовым полем и каждый отдельный адрес бит

#define set_bit(arr,x) ((arr[(x)>>3]) |= (0x01 << ((x) & 0x07)))
#define clear_bit(arr,x) (arr[(x)>>3] &= ~(0x01 << ((x) & 0x07)))
#define get_bit(arr,x) (((arr[(x)>>3]) & (0x01 << ((x) & 0x07))) != 0)

Просто использует индекс, который использует нижние 3 бита индекса, чтобы идентифицировать 8 разных позиций битов внутри каждого местоположения массива char, а верхние биты остатка - адреса, в которых расположено расположение бит, обозначенное символом x, Надеюсь, это поможет.

Edit1: Чтобы установить бит, вам нужно ИЛИ или целевое слово с другим словом с 1 в этой конкретной позиции бита и 0 во всех остальных с целью. Все 0 в других положениях гарантируют, что существующий 1 в цели будет таким же, как и во время OR, а 1 в определенных положениях гарантирует, что цель получит 1 в этой позиции. если у нас есть mask = 0x02 = 00000010 (1 байт), тогда мы можем ИЛИ это любому слову, чтобы установить бит pos

target = 1 0 1 1 0 1 0 0
OR       + + + + + + + +
mask     0 0 0 0 0 0 1 0
         ---------------
answer   1 0 1 1 0 1 1 0

Чтобы очистить бит, вам нужно И целевое слово с другим словом с 0 в этой конкретной позиции бит и всего 1. Все 1 во всех других положениях бит обеспечивают, чтобы во время И цель сохраняла свои 0 и 1 так, как они были в этих местах, а 0 в разрядной позиции, подлежащей очистке, также устанавливало бы эту позицию бит 0 в целевом слове. если у нас есть одна и та же маска = 0x02, тогда мы можем подготовить эту маску для очистки с помощью ~ mask

mask  = 0 0 0 0 0 0 1 0 
~mask = 1 1 1 1 1 1 0 1
AND     . . . . . . . .
target  1 0 1 1 0 1 1 0
        ---------------
answer  1 0 1 1 0 1 0 0

Ответ 4

Вопрос был о том, как реализовать в C, но так как все поиски "заменить биты" приводят сюда, я приведу свою реализацию в VB.Net. Это был unit тест. Для тех, кому интересно, как ToBinaryString расширение ToBinaryString: Convert.ToString(value,2)

''' <summary>
''' Replace the bits in the enumValue with the bits in the bits parameter, starting from the position that corresponds to 2 to the power of the position parameter.
''' </summary>
''' <param name="enumValue">The integer value to place the bits in.</param>
''' <param name="bits">The bits to place. It must be smaller or equal to 2 to the power of the position parameter.</param>
'''<param name="length">The number of bits that the bits should replace.</param>
''' <param name="position">The exponent of 2 where the bits must be placed.</param>
''' <returns></returns>
''' <remarks></remarks>'
<Extension>
Public Function PlaceBits(enumValue As Integer, bits As Integer, length As Integer, position As Integer) As Integer
    If position > 31 Then
        Throw New ArgumentOutOfRangeException(String.Format("The position {0} is out of range for a 32 bit integer.",
                                                            position))
    End If
    Dim positionToPlace = 2 << position
    If bits > positionToPlace Then
        Throw New ArgumentOutOfRangeException(String.Format("The bits {0} must be smaler than or equal to {1}.",
                                                            bits, positionToPlace))
    End If

    'Create  a bitmask (a series of ones for the bits to retain and a series of zeroes for bits to discard).'
    Dim mask As Integer = (1 << length) - 1
    'Use for debugging.'
    'Dim maskAsBinaryString = mask.ToBinaryString'

    'Shift the mask to left to the desired position'
    Dim leftShift = position - length + 1
    mask <<= leftShift
    'Use for debugging.'
    'Dim shiftedMaskAsBinaryString = mask.ToBinaryString'

    'Shift the bits to left to the desired position.'
    Dim shiftedBits = bits << leftShift
    'Use for debugging.'
    'Dim shiftedBitsAsBinaryString = shiftedBits.ToBinaryString'

    'First clear (And Not) the bits to replace, then set (Or) them.'
    Dim result = (enumValue And Not mask) Or shiftedBits
    'Use for debugging.'
    'Dim resultAsBinaryString = result.ToBinaryString'

    Return result
End Function

Ответ 5

  1. Примените маску против битового поля, чтобы сохранить биты, которые вы не хотите изменять. Это также очистит биты, которые вы будете менять.

  2. Убедитесь, что у вас есть битовое поле, содержащее только те биты, которые вы хотите установить/очистить.

  3. Либо используйте оператор "или" для двух битовых полей, либо просто добавьте их.

Например, если вы хотите изменить только биты с 2 по 5 на основе ввода от 0 до 15.

byte newVal = (byte)value & 0x0F;
newVal = (byte)value << 2;
oldVal = oldVal & 0xC3;
oldVal = oldval + newVal;

Ответ 6

Вам нужно будет делать это по одному бит за раз. Используйте то, что вы сейчас делаете, чтобы установить бит в один, и используйте следующее, чтобы установить что-то в 0:

reg_data &= ~ (1 << shift)

Ответ 7

Вы можете использовать эту динамическую логику для любого количества бит и в любом битовом поле. В основном, у вас есть 3 части в битовой последовательности чисел -

MSB_SIDE | CHANGED_PART | LSB_SIDE

CHANGED_PART может быть перемещен в крайнюю сторону MSB или LSB.

Шаги по замене количества битов следующие: 1. Возьмите только часть MSB_SIDE и замените все оставшиеся биты на 0. 2. Обновите новую битовую последовательность, добавив желаемую битовую последовательность в определенной позиции. 3. Обновите всю битовую последовательность с помощью LSB_SIDE исходной битовой последовательности.

org_no = 0x53513C;
upd_no = 0x333;
start_pos = 0x6, bit_len = 0xA;
temp_no = 0x0;

temp_no = org_no & (0xffffffff << (bit_len+start_pos));  //this is step 1
temp_no |= upd_no << start_pos;  //this is step 2
org_no = temp_no | (org_no & ~(0xffffffff << start_pos));  //this is step 3'

Примечание. Маскирование с 0xffffffff считается 32-битным. Вы можете изменить соответственно вашему требованию.