有人可以向我解释ARM按位操作吗?

Kri*_*oks 6 arm bit-manipulation bit-shift

有人可以像我五岁一样向我解释ARM位移吗?我对涉及非十进制数系统的任何事情都知之甚少,因此理解位移和位运算符的概念对我来说很难.

以下每种情况会做什么以及为什么(最终会出现R3什么以及在位级别的幕后会发生什么)?

/** LSL **/
mov r0, #1
mov r3, r0, LSL#10

/** LSR **/
mov r0, #1
mov r3, r0, LSR#10

/** ORR **/
mov r0, #1
mov r1, #4
orr r3, r1, r0

/** AND **/
mov r0, #1
mov r1, #4
and r3, r1, r0

/** BIC **/
mov r0, #1
mov r1, #4
bic r3, r1, r0
Run Code Online (Sandbox Code Playgroud)

PS.不要用C位运算符来解释它.我不知道他们做了什么或者(在>>,<<,|,&的).

old*_*mer 19

真值表,两个输入,左边的两个数字和一个输出,右边的数字:

要么

a b  c     
0 0  0
0 1  1
1 0  1
1 1  1
Run Code Online (Sandbox Code Playgroud)

左边的两个输入a和b代表输入的四种可能组合,不多于列表.

考虑1表示真,0表示假.在这种情况下,单词OR表示如果OR b为真,则c为真.正如您在表格中看到的那样,如果a或b为真,则水平地为c,则c为真.

a b  c
0 0  0
0 1  0
1 0  0
1 1  1
Run Code Online (Sandbox Code Playgroud)

并且意味着如果AND b都为真则它们都必须为真,则c为真.上面只存在一种情况.

现在取两个字节0x12和0x34,十进制是18和52但我们真的不太关心十进制.我们关心二进制0x12是0b00010010而0x34是0b00110100.汇编语言中的AND,OR和XOR等按位运算符意味着从每个操作数中取一位,并将结果赋予相同的位位置.它不喜欢添加你有这样的东西加上等于等等.

所以我们排队比特

0b00010010    0x12
0b00110100    0x34
Run Code Online (Sandbox Code Playgroud)

所以倾斜你的头部,就像你要用左手拿着炸玉米饼咬一口,然后想象上面的真相表.如果我们看右边的两位,它们是0和0,接下来的两位是1和0,依此类推.因此,如果我们想要执行OR运算,则规则是如果a或b为真,则c,结果为真

   0b00010010
   0b00110100
OR ==========
   0b00110110
Run Code Online (Sandbox Code Playgroud)

磁头向右倾斜,最低有效位(数字中的一列中的位)0或0 = 0,两者均未设置.下一列(两列)1或0 = 1至少有一个为真.等等

0x12或0x34 = 0x36

在手臂组装中

mov r0,#0x12
mov r1,#0x34
orr r2,r0,r1
Run Code Online (Sandbox Code Playgroud)

在或运算之后r2将保持值0x36.

现在让我们和那些数字

    0b00010010
    0b00110100
AND ==========
    0b00010000
Run Code Online (Sandbox Code Playgroud)

记住我们的真值表和a和b都必须为真的规则(a 1)我们将头部向右倾斜,0和0为0,两者都不是真的.通过检查,只有一列的输入带有1,即16s列.这给我们留下了0x12和0x34 = 0x10

在手臂组装中

mov r0,#0x12
mov r1,#0x34
and r2,r0,r1
Run Code Online (Sandbox Code Playgroud)

现在我们进入BIC指令.哪个代表按位清晰,希望稍微有点意义.胳膊上的Bic是a bded而不是b.不是另一个真值表,而只是一个输入和一个输出

a  c
0  1
1  0
Run Code Online (Sandbox Code Playgroud)

只有一个输入,我们只有两个选择0和1,1为真0为假.NOT表示如果不是则c为真.当a不为真时c为真,当a为真时c不为真.基本上它反转了.

bic做的是有两个输入a和b,操作是c = a AND(NOT b)所以真值表是:

和(不是b)

a b  c
0 1  0
0 0  0
1 1  0
1 0  1
Run Code Online (Sandbox Code Playgroud)

我开始使用AND真值表,然后记下b位,其中b在AND真值表中为0,我将其设为1,其中b在AND真值表中为1,我将其设为0.

所以在0x12和0x34上的双操作是

    0b00010010
    0b00110100
BIC ==========
    0b00000010
Run Code Online (Sandbox Code Playgroud)

为什么它被称为有点清楚?了解这使得它更容易使用.如果你查看真值表并考虑第一和第二输入.在第二个b输入为1的情况下,输出为0.其中第二个输入b为0,输出本身未经修改.所以那个真值表或操作正在做的是说任何地方b被设置为清零或者是否为A中的那些位.所以如果我有数字0x1234并且我想将低8位归零,那么我将用0x00FF进行BIC.你的下一个问题是为什么不和那个0xFF00?(分析AND真值表,看看无论b是1,你都保持一个值,而b是0,你输出为零).ARM至少在传统上使用32位寄存器和固定的32位指令集.即时指示

mov r0,#0x12
Run Code Online (Sandbox Code Playgroud)

在臂内限制为8个非零位移位数字内的任何位置,将稍微移位.所以,如果我的值为0x12345678,并希望将低8位清零,我可以这样做

; assume r0 already has 0x12345678
bic r0,r0,#0xFF
Run Code Online (Sandbox Code Playgroud)

要么

; assume r0 already has 0x12345678
mov r1,#0xFF000000
orr r1,r1,#0x00FF0000
orr r1,r1,#0x0000FF00
;r1 now contains the value 0xFFFFFF00
and r0,r0,r1
Run Code Online (Sandbox Code Playgroud)

要么

; assume r0 already contains 0x12345678
ldr r1,my_byte_mask
and r0,r0,r1
my_byte_mask: .word 0xFFFFFF00
Run Code Online (Sandbox Code Playgroud)

与使用移动和两个orrs相比,这并不可怕,但仍然比bic解决方案燃烧更多的时钟周期,因为你烧掉额外的内存周期从ram读取my_byte_mask,这可能需要一段时间.

要么

; assume r0 already contains 0x12345678
mvn r1,#0xFF
and r0,r0,r1
Run Code Online (Sandbox Code Playgroud)

最后一个不是妥协.请注意,arm文档中的mvn是按位而不是立即,这意味着rx = NOT(立即).这里的立即是0xFF.NOT(0xFF)表示反转所有位,它是一个32位寄存器,这意味着0xFFFFFF00是NOT(0xFF)的结果,这就是寄存器r1在执行和之前得到的结果.

这就是为什么bic在ARM指令集中占有一席之地,因为有时使用bic指令而不是和指令来掩码(掩码= AND用于使某些位为零)需要更少的指令或时钟周期.

我使用掩码这个词作为一个概念,使得数字为零,而其他人则不然.orring可以被认为是在第一个中创建位而只留下其他位,如果你看到OR真值表,任何时候b是1然后c是1.所以0x12345678 OR 0x000000FF导致0x123456FF第二位中的位操作数已设置.是的,无论何时在OR真值表中设置a然后设置输出都是正确的,但是很多时候当你使用这些按位操作时你有一个操作数你要做什么,设置一定数量的位如果没有修改其余部分或将一定数量的比特设置为零而不修改其余部分,或者除了一定数量的比特之外你想要将所有比特归零.当以这种方式使用时,你有一个操作数进入你想要操作的那个,你根据你想要的整体效果创建第二个操作数,例如在C中我们只想保留低位字节有一个参数,一个参数输出功能:

unsigned int keep_lower_byte ( unsigned int a )
{
    return(a&(~0xFF));
}
Run Code Online (Sandbox Code Playgroud)

〜表示不是这样~0xFF,对于32位数字表示0xFFFFFF00然后&表示AND,所以我们返回一个&0xFFFFFF00.a是唯一真正的操作数进入,我们根据我们想要的操作发明了第二个...大多数按位操作你可以在指令中交换操作数,一切都结果没问题,像ARM的两个指令虽然操作数是按照一定的顺序,就像减法一样,你必须使用正确的操作数顺序.

转移......有两种,逻辑和算术.逻辑是最简单的,是你在C中使用>>或<<时得到的.

从0x12开始,即0b00010010.向左移动三个位置(0x12 << 3)表示

00010010 < our original number 0x12
0010010x < shift left one bit location
010010xx < shift left another bit location
10010xxx < shift left a third bit location
Run Code Online (Sandbox Code Playgroud)

什么位被"移入"到空位置,上面的x',根据操作而变化.对于C编程,它总是为零:

00010010 < our original number 0x12
00100100 < shift left one bit location
01001000 < shift left another bit location
10010000 < shift left a third bit location
Run Code Online (Sandbox Code Playgroud)

但有时候(通常每个指令集都支持旋转和移位)还有其他方法可以移动,而且差异与你转移到空位的位有关,有时你移动到末端的位并不总是只是消失,有时你将它保存在一个特殊的位持有人位置.

某些指令集只有一个位移位意味着对于您编程的每条指令,您只能移位一位,因此上面将是3条指令,一次一位.其他指令集(如arm)允许您使用单个指令,并在指令中指定要在该方向上移位多少位.所以左转三个

mov r0,#0x12
mov r3,r0,lsl#3 ; shift the contents of r0 3 bits to the left and store in r3
Run Code Online (Sandbox Code Playgroud)

在lsr和asr,逻辑右移和算术右移之间演示了你移入的变化(你会看到没有asl,算术移位因为没有意义,一些汇编器会允许你使用asl指令)但将其编码为lsl).

一个LOGICAL右移:

00010010 - our original number 0x12
x0001001 - shifted right one bit
xx000100 - shifted right another bit
xxx00010 - shifted right another bit
Run Code Online (Sandbox Code Playgroud)

与C一样,有一个版本以零的形式移动,即逻辑右移,以零移动

00010010 - our original number 0x12
00001001 - shifted right one bit
00000100 - shifted right another bit
00000010 - shifted right another bit
Run Code Online (Sandbox Code Playgroud)

ARITHMETIC右移意味着保留"符号位"符号位是什么?如果你没有,你还需要学习两个补码.基本上,如果您将位模式/值视为二进制补码数,则最高位(左侧的位)是符号位.如果为0,则数字为正数,1为数字为负数.您可能已经注意到,一位左移与二乘相同,右移与二次相同.0x12 >> 1 = 0x9,18 >> 1 = 9但如果我们要移位怎么办?减去2到右边,减去2是使用字节的0xFE或0b11111110.使用C样式逻辑右移0xFE >> 1 = 0x7F,或使用十进制-2 >> 1 = 0x127.不幸的是,我们无法在单个操作中用C解决这个问题,但是在汇编中我们可以使用算术移位,假设你的指令集有一个,那么arm就是

ARITHMETIC右移

s1100100 - our starting value s is the sign bit whatever that is 0 or 1
ss110010 - one shift right
sss11001 - another shift right
ssss1100 - another shift right
Run Code Online (Sandbox Code Playgroud)

因此,如果我们开始时符号位s是0,那么数字是01100100那么

01100100 - our starting value
00110010 - one shift right
00011001 - another shift right
00001100 - another shift right
Run Code Online (Sandbox Code Playgroud)

但如果那个标志位是一个

11100100 - our starting value
11110010 - one shift right
11111001 - another shift right
11111100 - another shift right
Run Code Online (Sandbox Code Playgroud)

我们可以解决向右偏移的0xFE:

11111110 - 0xFE a minus 2 in twos complement for a byte
11111111 - shifted right one
Run Code Online (Sandbox Code Playgroud)

所以在伪代码0xFE ASR 1 = 0xFF,-2 ASR 1 = -1.-2除以2 = -1

您需要自己阅读的最后一件事与旋转和/或从末端移位的位发生的事情有关.一个向右移动的lsbit被移动到"数字的末端",就像块被滑动的一个桌子而掉落的那个可能只是进入"比特桶"(以太,天堂或地狱,其中一个位置当他们从这个世界消失时去死吧.但是某些指令集中的某些指令会将该位移位并将其置于进位标志中(在加和减时读取),不是因为它必然是进位而是因为在alu和进位中有状态位是有道理的.现在,旋转是什么,是让说你有一个8位处理器和你旋转一个位,该位脱落年底的土地中进位,位在另一侧移位是什么在进位前操作.基本上它是音乐椅,位于椅子周围,一个人站着站立,站立的人是携带位,椅子上的人是寄存器中的位.为什么这有用呢?假设我们有像Atmel AVR这样的8位处理器,但是想要进行64位移位.64位需要8位,8位寄存器,比如说我在这8个寄存器中有64位数,我想做一个64位左移一位.我将从最低有效字节开始,并执行一个将零移入的lsl,但是移位的位进入进位位.那么下一个最重要的字节我做一个rol,向左旋转一位,进入的位是从前一个字节开始的位,然后输出的位进入进位.我重复其他字节的rol指令,查看16位移位:

00100010 z0001000 - our original number
00100010 z 0001000 - lsl the least significant byte, the ms bit z is in carry
0100010z 00010000 - rotate left the most significant byte pulling the z bit from carry

00100010z0001000 - if it had been a 16 bit register
0100010z00010000 - a logical shift left on a 16 bit with a zero coming in on the left
Run Code Online (Sandbox Code Playgroud)

这就是旋转的目的,这就是为什么装配手册无法告诉你在执行逻辑操作时修改了哪些标志.