Как правильно выбрать сдвиг влево в битовых операциях?

Я изучаю программирование с нуля на с ++, и это часто включает в себя установку части 32-битного адреса аппаратного регистра для некоторой комбинации.

Например, для вывода ввода-вывода я могу установить 15-17-й бит в 32-битном адресе в 001 пометить контакт как выходной контакт.

Я видел код, который делает это, и я наполовину понимаю это на основе объяснения другого ТАК вопрос.

# here ra is a physical address
# the 15th to 17th bits are being
# cleared by AND-ing it with a value that is one everywhere
# except in the 15th to 17th bits
ra&=~(7<<12);

Другой пример:

# this clears the 21st to 23rd bits of another address
ra&=~(7<<21);

Как выбрать 7 и как выбрать количество бит для сдвига влево?

Я попробовал это в питон чтобы увидеть, смогу ли я понять это

bin((7<<21)).lstrip('-0b').zfill(32)
'00000000111000000000000000000000'
# this has 8, 9 and 10 as the bits which is wrong

1

Решение

Если вы хотите изолировать и изменить некоторые биты в регистре, но не все, что вам нужно для понимания побитовых операций, таких как и, или, и xor, и не работать с одиночным битовым столбцом, бит 3 каждого операнда используется для определения бита 3 результат, никакие другие биты не участвуют. Итак, у меня есть несколько битов в двоичном коде, представленных буквами, так как каждый из них может быть 1 или ноль

jklmnopq

Таблицу истинности операций и операций, которую вы можете посмотреть, все, что с нулем — это ноль.

   jklmnopq
&  01110001
============
0klm000q

все, что связано с одним, является чем-то, что все, что связано с нулем, является само по себе.

   jklmnopq
|  01110001
============
j111nop1

так что если вы хотите изолировать и изменить два бита в этой переменной / регистре, скажем, биты 5 и 6 и изменить их на 0b10 (2 в десятичном формате), то общий метод состоит в том, чтобы и их с нулем тогда или их с желаемым значением

   76543210

jklmnopq
&  10011111
============
j00mnopq

jklmnopq
|  01000000
============
j10mnopq

Вы могли бы присвоить бит 6 с 1 и с добавленным битом 5 с нулем, но это относится к значению, на которое вы хотите изменить их, в общем, мы думаем, что я хочу изменить эти биты на 2, поэтому использовать это значение 2 Вы хотите обнулить биты, а затем принудительно ввести 2 в эти биты, а затем сделать их равными нулю, а затем направить 2 в биты. родовой.

В с

x = read_register(blah);
x = (x&(~(3<<5)))|(2<<5);
write_register(blah,x);

давайте углубимся в это (3 << 5)

00000011
00000110 1
00001100 2
00011000 3
00110000 4
01100000 5

76543210

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

использование x = ~ x инвертирует эти биты логической операцией not.

01100000
10011111

Теперь у нас есть маска, которую мы хотим, и наш регистр, как показано выше, обнуляя биты, оставляя в покое остальные j00mnopq

Теперь нам нужно подготовить биты к или (2<<5)

00000010
00000100 1
00001000 2
00010000 3
00100000 4
01000000 5

Давая битовый шаблон, который мы хотим изменить, давая j10mnopq, который мы записываем обратно в регистр. Опять же, j, m, n, … биты — это биты, они либо единица, либо ноль, и мы не хотим их менять, поэтому мы выполняем эту дополнительную работу по маскировке и сдвигу. Вы можете / будете иногда видеть примеры, которые просто пишут_регистр (бла, 2<<5); либо потому, что они знают состояние других битов, знают, что они не используют эти другие биты, а ноль в порядке / желателен, либо не знают, что они делают.

x read_register(blah); //bits are jklmnopq
x = (x&(~(3<<5)))|(2<<5);

z = 3
z = z << 5
z = ~z
x = x & z
z = 2
z = z << 5
x = x | zz = 3

z = 00000011

z = z << 5

z = 01100000

z = ~z

z = 10011111

x = x & z

x = j00mnopq

z = 2

z = 00000010

z = z << 5

z = 01000000

x = x | z

x = j10mnopq

если у вас есть 3-битное поле, то двоичный файл равен 0b111, который в десятичном виде представляет собой число 7 или шестнадцатеричный 0x7. 4-битное поле 0b1111, которое является десятичным 15 или шестнадцатеричным 0xF, так как после 7, проще использовать шестнадцатеричное IMO. 6-битное поле 0x3F, 7-битное поле 0x7F и т. Д.

Вы можете пойти дальше и попытаться быть более общим. Если есть регистр, который управляет некоторой функцией для выводов gpio от 0 до 15. скажем, начиная с бита 0. Если вы хотите изменить свойства для вывода 5 gpio, то это будут биты 10 и 11, 5 * 2 = 10, есть два штифтов так 10, а следующий 11. Но в общем вы могли бы:

x = (x&(~(0x3<<(pin*2)))) | (value<<(pin*2));

так как 2 является степенью 2

x = (x&(~(0x3<<(pin<<1)))) | (value<<(pin<<1));

Оптимизация, которую может выполнить компилятор, если pin не может быть уменьшен до определенного значения во время компиляции.

но если бы это было 3 бита на поле, и поля начинаются с нуля

x = (x&(~(0x7<<(pin*3)))) | (value<<(pin*3));

который компилятор может сделать умножение на 3, но, возможно, вместо этого просто

pinshift = (pinshift<<1) | pinshift;

чтобы получить умножить на три. зависит от компилятора и набора инструкций.

В целом это называется записью с изменением чтения, когда вы что-то читаете, изменяете некоторые из них, затем записываете обратно (если бы вы изменяли все это, вам не нужно было бы беспокоиться о чтении и изменении, вы бы записали все новое значение). И люди скажут маскирование и сдвиг, чтобы в общем покрывать изолирующие биты в переменной либо для целей модификации, либо, если вы хотите прочитать / посмотреть, какими были эти два бита выше, вы бы

x = read_register(blah);
x = x >> 5;
x = x & 0x3;

или сначала замаскируйте, а затем сместите

x = x & (0x3<<5);
x = x >> 5;

шесть из полдюжины других, в общем, оба равны, некоторые наборы команд могут быть более эффективными, чем другие (или могут быть равными, а затем сдвигаться или сдвигаться затем и). Один может иметь больше смысла визуально для некоторых людей, чем для других.

Хотя технически это порядковая вещь, так как в некоторых процессорах бит 0 является наиболее значимым битом. В C AFAIK бит 0 является наименее значимым битом. Если / когда руководство показывает биты, расположенные слева направо, вы хотите, чтобы ваши сдвиги вправо и влево соответствовали этому, так как выше я показал 76543210, чтобы указать документированные биты и связал это с jklmnopq, и это была информация слева направо, которая имела значение продолжить разговор об изменении битов 5 и 6. в некоторых документах будут использоваться обозначения в стиле verilog или vhdl 6: 5 (означают биты с 6 по 5 включительно, имеет смысл, скажем, 4: 2, означая биты 4,3,2) или [6 до 5], более вероятно, что вы просто увидите визуальную картинку с прямоугольниками или строками, чтобы показать вам, какие биты являются каким полем.

1

Другие решения

7 (основание 10) выбрано, поскольку его двоичное представление равно 111 (7 в основании 2).

Что касается того, почему биты 8, 9 и 10 установлены, потому что вы читаете не в том направлении. Двоичное, как и обычное основание 10, считается справа налево.

(Я оставил это как комментарий, но репутация недостаточно высока.)

2

Как выбрать 7

Вы хотите очистить три смежных бита. Три соседних бита в нижней части слова равны 1 + 2 + 4 = 7.

и как мне выбрать количество бит для сдвига влево

Вы хотите очистить биты 21-23, а не биты 1-3, поэтому вы сдвигаетесь влево еще на 20.

Оба ваших примера неверны. Чтобы очистить 15-17, вам нужно сдвинуть влево на 14, а для очистки 21-23 вам нужно сдвинуть влево на 20.

это 8, 9 и 10 …

Нет, это не так. Вы считаете не с того конца.

0
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector