如何在位明智的操作中选择正确的左移

How to choose the correct left shift in bit wise operations?

本文关键字:选择 左移 操作      更新时间:2023-10-16

我正在学习C 中的裸机编程,并且通常涉及将32位硬件寄存器地址的一部分设置为某种组合。

例如,对于IO PIN,我可以在32位地址中将15至17位设置为001,以将PIN标记为输出引脚。

我已经看到了执行此操作的代码,而我一半的理解是基于对另一个问题的解释。

# 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 以及如何选择要向左移动的位数?

我在 python 中尝试了一下,看看我是否可以弄清楚

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

选择7(基本10)是因为其二进制表示为111(基本2中的7)。

至于为什么它是8、9和10的位置,这是因为您从错误的方向上读书。二进制,就像普通基数一样,右至左计数。

(我将其作为评论,但声誉不够高。)

如果要隔离并更改寄存器中的某些位,但不是所有需要理解诸如和and xor之类的位操作每个操作数用于确定结果的位3,不涉及其他位。因此,我有一些字母表示的二进制部分,因为它们每个都可以是1或零

jklmnopq

您可以查找的真相表和操作真相表,任何用零和零的东西都为零,任何一个和一个与一个的东西都是

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

任何一个带有一个的东西都是一个零的东西。

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

因此,如果您想在此变量/寄存器中隔离和更改两个位,则表示位5和6,然后将它们更改为0b10(十进制为2个),则常用方法是且它们具有零,或者它们与它们一起使用所需的值

   76543210
   jklmnopq
&  10011111
============
   j00mnopq
   jklmnopq
|  01000000
============
   j10mnopq

您可以用1个和零的位置为零,但这特定于您想更改它们的价值,通常我们认为我想将这些位更改为2,以便使用该值2您要为零地点,然后将2强将其强加于这些位,然后将它们变成零,然后将2个orr到块上。通用。

在C

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

让我们深入研究(3&lt;&lt; 5)

00000011
00000110 1
00001100 2
00011000 3
00110000 4
01100000 5
76543210

将两个放在我们感兴趣的位顶上/p>

使用x = 〜x反转这些位是逻辑上的,而不是操作。

01100000
10011111

现在,我们有了我们想要的掩模,并使用上述方式显示我们的登记册

现在,我们需要将钻头准备为或(2&lt;&lt; 5)

00000010
00000100 1
00001000 2
00010000 3
00100000 4
01000000 5

给出我们想要提供的位模式,以提供我们将其写回寄存器的J10MNOPQ。同样,J,M,N,...位是碎片,它们是一个或零,我们不想更改它们,因此我们进行了额外的掩盖和转移工作。您可能会/有时会看到简单地写入_register的示例(blah,2&lt;&lt; 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 | z

z = 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,使用HEX IMO更容易。6位字段0x3f,7位字段0x7f等。

您可以以一种更通用的方式采取进一步的方式。如果有一个寄存器可以控制gpio引脚0到15的函数。从0。1销钉SO SO 10和下一个11。但是通常您可以:

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

因为2是2

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

一个优化,如果在编译时间无法将引脚还原为特定值,则编译器可能会做。

但是,如果每个字段是3位,并且字段开始与位零对齐

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

编译器可能会乘以3的

,但也许只是

pinshift =(pinshift&lt;&lt; 1)| pinshift;

将乘以三个。取决于编译器和指令集。

总体而言,这称为读取一些读取内容时,修改了一些,然后写回(如果您要修改所有内容)。人们会说掩盖和转移以用于修改目的的变量中,或者是否想阅读/查看上面的那两块是什么,您会

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

或蒙版,然后移动

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

六个二十二的六个,两者通常是相等的,某些指令集可能比另一个指令更有效(或者可能是相等的,然后移动或移动,然后移动)。一个人对某些人而不是另一个人的视觉效果更有意义。

从技术上讲,这是一个结束的事情,因为某些处理器位0是最重要的位。在C afaik位0中,0是最不重要的位。如果/当手册显示从左到右放置的位时,您希望左右移动以匹配该位,因此我显示了76543210,以指示已记录的位,并与JKLMNOPQ相关联,这是重要的信息,这是重要的信息要继续有关修改位5和6的对话。有些文档将使用Verilog或VHDL样式符号6:5(含义6至5包含在内的位,更有意义,例如4:2含义含量4,3,2)或[6降至5],更有可能只看到带有框或行的视觉图片,以向您展示哪些位置。

如何选择7

您想清除三个相邻位。单词底部的三个相邻位是1 2 4 = 7。

以及如何选择要移动的位数

您想清除位21-23,而不是1-3的位,所以您移动另一个。

您的两个示例都是错误的。要清除15-17,您需要向左移动14,才能清除21-23,您需要向左移动20。

这有8、9和10 ...

不。您正在从错误的终端计数。