簡體   English   中英

如何在逐位操作中選擇正確的左移?

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

我正在學習c ++中的裸機編程,它通常涉及將32位硬件寄存器地址的一部分設置為某種組合。

例如,對於IO引腳,我可以將32位地址中的第15位至第17位設置為001以將引腳標記為輸出引腳。

我已經看到了這樣做的代碼,我根據對另一個SO問題的解釋來理解它

# 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設置它是因為你從錯誤的方向讀取。 二進制,就像正常的基數10一樣,從右到左計數。

(我將此作為評論,但聲譽不夠高。)

如果要隔離並更改寄存器中的某些位但不是所有需要理解像和和/或xor這樣的按位操作而不是單個位列操作,則每個操作數的第3位用於確定第3位的結果,不涉及其他位。 所以我有一些用字母表示的二進制位,因為它們都可以是1或0

jklmnopq

你可以查找和操作真值表,任何一個零的東西都是零,任何一個本身就是一個

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

任何與之相關的東西都是一個零的東西。

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

因此,如果你想隔離和改變這個變量/寄存器中的兩個比特,比如第5和第6位,並將它們改為0b10(十進制中的2),那么通常的方法是將它們設為零或者它們具有所需的值

   76543210

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

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

你可以使用1和0位的orred第6位,但是這是特定於你想要更改它們的值,一般來說我們認為我想將這些位更改為2,所以要使用該值2你想把這些比特歸零,然后將2強制加到這些比特上,然后將它們歸零,然后將2或2加到比特上。 通用的。

在c

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

將兩個1置於我們感興趣的位之上,但是使用該值隔離位並將其他位置混淆,以便將其歸零並且不要混淆寄存器中的其他位,我們需要將這些位反轉

使用x = ~x將這些位反轉為邏輯非操作。

01100000
10011111

現在我們有了我們想要的掩碼和我們的寄存器,如上所示,將有問題的位歸零,同時留下其他的j00mnopq

現在我們需要准備位或(2 << 5)

00000010
00000100 1
00001000 2
00010000 3
00100000 4
01000000 5

給出我們想要的位模式給j10mnopq,我們寫回寄存器。 同樣,j,m,n,...位是位,它們是一個或零,我們不想改變它們,所以我們做了額外的屏蔽和轉移工作。 您可能/有時會看到只是write_register(blah,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 | 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時,它更容易使用十六進制IMO。 6位字段0x3F,7位字段0x7F,依此類推。

您可以采取進一步的方式嘗試更通用。 如果有一個寄存器控制gpio引腳0到15的某些功能,則從位0開始。如果你想改變gpio引腳5的屬性那么那就是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));

如果引腳在編譯時無法減少到特定值,編譯器可能會做的優化。

但如果每個字段為3位,則字段開始與位0對齊

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;

六個中的六個中的一個,一般都是相同的,一些指令集可能比另一個更有效(或者可能是相等然后移位,或者然后移位)。 人們可能會在視覺上對某些人而不是其他人更有意義。

雖然從技術上講這是一個endian事物,因為一些處理器位0是最重要的位。 在C AFAIK中,位0是最低有效位。 如果/當手冊顯示從左到右排列的位時,您希望左右移位與之匹配,如上所示,我顯示76543210以指示記錄的位並與jklmnopq相關聯,這是重要的從左到右的信息繼續關於修改位5和6的對話。一些文檔將使用verilog或vhdl樣式表示法6:5(意思是位6到5包含,更有意義,比如4:2表示位4,3,2)或[6 downto 5],更有可能只看到帶有方框或線條的視覺圖片,以顯示哪些位是什么字段。

我該如何選擇7

您想要清除三個相鄰的位。 一個字底部的三個相鄰位是1 + 2 + 4 = 7。

以及如何選擇向左移位的位數

你想要清除21-23位,而不是1-3位,所以你向左移動20位。

你的兩個例子都錯了。 要清除15-17你需要向左移動14,要清除21-23你需要向左移動20。

這有8,9和10 ......

不,不。 你是從錯誤的一端算起來的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM