Я изучаю программирование с нуля на с ++, и это часто включает в себя установку части 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
Если вы хотите изолировать и изменить некоторые биты в регистре, но не все, что вам нужно для понимания побитовых операций, таких как и, или, и 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], более вероятно, что вы просто увидите визуальную картинку с прямоугольниками или строками, чтобы показать вам, какие биты являются каким полем.
7 (основание 10) выбрано, поскольку его двоичное представление равно 111 (7 в основании 2).
Что касается того, почему биты 8, 9 и 10 установлены, потому что вы читаете не в том направлении. Двоичное, как и обычное основание 10, считается справа налево.
(Я оставил это как комментарий, но репутация недостаточно высока.)
Как выбрать 7
Вы хотите очистить три смежных бита. Три соседних бита в нижней части слова равны 1 + 2 + 4 = 7.
и как мне выбрать количество бит для сдвига влево
Вы хотите очистить биты 21-23, а не биты 1-3, поэтому вы сдвигаетесь влево еще на 20.
Оба ваших примера неверны. Чтобы очистить 15-17, вам нужно сдвинуть влево на 14, а для очистки 21-23 вам нужно сдвинуть влево на 20.
это 8, 9 и 10 …
Нет, это не так. Вы считаете не с того конца.