2012-01-28 83 views
5

有人可以解释ARM位移给我像我五?对于涉及非十进制数字系统的任何事情我都有很差的理解,因此理解位移和位运算符的概念对我来说很困难。有人可以向我解释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 

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

回答

15

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

OR

a b c  
0 0 0 
0 1 1 
1 0 1 
1 1 1 

左侧两个输入a和b表示的四种可能的组合的投入,没有更多不少于这个清单。

考虑1代表真,0代表假。在这种情况下,OR这个词意味着如果一个OR b是真的,那么c是真的。正如你在表中看到的那样,如果a或b是真的,那么c就是水平的。

a b c 
0 0 0 
0 1 0 
1 0 0 
1 1 1 

而且意味着他们都有如果A和B都为真,那么c为真令人难以置信。上面只存在一种情况。

现在取两个字节0x12和0x34,其中十进制是18和52,但我们并不十分关心小数。我们关心二进制0x12是0b00010010而0x34是0b00110100。像汇编语言中的AND和OR和XOR这样的按位运算符意味着您从每个操作数取一位,并将结果放在相同的位位置。它不像加上你有这样的事情,加上等于等等等等。

所以我们排队位

0b00010010 0x12 
0b00110100 0x34 

于是转过头sidways像你要采取咬了你,左手持塔科和可视化上面的真值表。如果我们看右边的两位,它们是0和0,接下来的两位是1和0,依此类推。所以,如果我们想要做或操作,该规则是,如果A或B为真,那么C,其结果,是真正的

0b00010010 
    0b00110100 
OR ========== 
    0b00110110 

头向右倾斜,至少显著位(在那些位列中的数字)0或0 = 0,都没有设置。下一列(二列)1或0 = 1至少有一个是真的。等这样

0×12 OR 0x34 = 0x36

在臂组件,这将是

mov r0,#0x12 
mov r1,#0x34 
orr r2,r0,r1 
的或操作R2将持有的值0x36之后

现在,让我们和这些数字

0b00010010 
    0b00110100 
AND ========== 
    0b00010000 

记住我们的真值表和规则A和B都为真(1)我们我们的头向右倾斜,0,0 0,无论是是不正确的。并且通过检查,只有一列具有1个16s列的输入。这给我们留下了0×12和0x34 = 0x10的

在臂组件,这将是

mov r0,#0x12 
mov r1,#0x34 
and r2,r0,r1 

现在我们要在BIC指令。其中代表清楚,希望稍微有意义。手臂上的BIC与B不同。那是另一真值表,但只有一个输入和一个输出

a c 
0 1 
1 0 

只有一个输入我们只有两个选择,0和1,1为真0是假的。不意味着如果没有,那么c是真实的。当a不正确时c是真实的,当a正确时c不正确。基本上它倒置。

什么BIC确实是有两个输入A和B,操作C = A AND(非B),因此该真值表将是:

一个AND(非B)

a b c 
0 1 0 
0 0 0 
1 1 0 
1 0 1 

我开始与AND真值表,然后注意到b比特,其中b是在AND真值表中的0我让它成为1其中b是AND真值表中的1我使它成为0.

所以0x12和0x34上的bic操作是

0b00010010 
    0b00110100 
BIC ========== 
    0b00000010 

为什么它被称为位清晰?了解这一点使它更容易使用。如果你看看真值表,并考虑第一个和第二个输入。在第二个输入是1的情况下,输出是0.在第二输入b是0的情况下,输出本身是未修改的。因此,真值表或操作所做的是在任何地方将b设置为清零或将A中的那些位置为零。因此,如果我具有数字0x1234并且我想将较低的8位置零,那么我将BIC与0x00FF进行比较。而你的下一个问题是为什么不与AND 0xFF00? (分析AND真值表,看看哪里b是1,你保持a值原样,并且无论b是0,你的输出为零)。 ARM至少使用32位寄存器和固定的32位指令集。立即指令

mov r0,#0x12 

支持限制在数字中任意位置移动的8位非零位,将会移位一位。所以,如果我有值0×12345678,想零出低8位,我可以做到这一点

; assume r0 already has 0x12345678 
bic r0,r0,#0xFF 

; 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 

; assume r0 already contains 0x12345678 
ldr r1,my_byte_mask 
and r0,r0,r1 
my_byte_mask: .word 0xFFFFFF00 

这并不可怕,比起使用移动和两个orrs,但仍比bic解决方案耗费更多的时钟周期,因为您将额外的内存循环从ram中读取my_byte_mask,这可能需要一段时间。

; assume r0 already contains 0x12345678 
mvn r1,#0xFF 
and r0,r0,r1 

这最后一个是不是一个坏compromize。请注意arm文档中的mvn不是立即数,这意味着rx = NOT(立即数)。这里的直接是0xFF。 NOT(0xFF)表示反转所有位,它是一个32位寄存器,我们将这样做,这意味着0xFFFFFF00是NOT(0xFF)的结果,这就是寄存器r1在执行和之前得到的结果。

所以这就是为什么BIC在ARM指令集的地方,因为有时它需要较少的指令或时钟周期掩模(掩模= AND用于制造某些位零)使用BIC指令,而不是与指令。

我用单词掩码作为一个概念,使数字零位留下其他人。如果你在任何时候b是1时看看OR真值表,那么可以认为它是在第一位中进行比特,然后c是1.因此,0x12345678或0x000000FF会导致0x123456FF中的第二个比特操作数被设置。是的,任何时候在OR真值表中设置a然后设置输出也是真实的,但是当使用这些按位操作时,您有很多时间需要对某个操作数进行操作,设置一定数量的位到其中一个而不修改剩余的位或者将一定数量的位设置为零而不修改其余位或者您想除了一定数量的位之外将所有位置零。当使用这种方式时,您将有一个操作数进入您想要操作的位置,并根据您想要的整体效果创建第二个操作数,例如在C中,如果我们只想保留低位字节有,一个参数功能的有一个参数:

unsigned int keep_lower_byte (unsigned int a) 
{ 
    return(a&(~0xFF)); 
} 

〜意味着不仅如此〜0xFF时,为32位数字指0xFFFFFF00然后&手段和,所以我们返回& 0xFFFFFF00。 a是唯一真正的操作数,我们根据我们想要做的操作发明了第二个操作数...大多数按位操作可以交换指令中的操作数,一切都变好了,但是ARM的bic指令虽然操作数是按照某种顺序,就像减法一样,你必须使用正确的操作数顺序。

移位...有两种,逻辑和算术。逻辑是最容易的,当你使用>>或< <时得到的结果是C。

从0x12开始,它是0b00010010。搬那个三个位置向左(0×12 < < 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 

什么位获得“转移”到空的位置,上面的x'es,变化的基础上的操作。对于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 

但有时(通常每指令集支持旋转和移位)还有其他方法转移,差异有什么位你转移到做空位,并且有时候你转移到最后的位并不总是会消失,有时你会将它保存在一个特殊的位持有者位置。

一些指令集只有一个位移意味着你编程的每个指令只能移动一位,所以上面是3条指令,一次一位。其他指令集(如arm)允许您只有一条指令,并在指令中指定要在该方向上移位多少位。所以左移位三个

mov r0,#0x12 
mov r3,r0,lsl#3 ; shift the contents of r0 3 bits to the left and store in r3 

您在证明LSR和ASR,逻辑右移和算术右移之间切换一下这个变(你会看到,有没有ASL算术左移,因为这使没有意义,一些汇编程序将允许您使用asl指令,但将其编码为lsl)。

逻辑右移:

00010010 - our original number 0x12 
x0001001 - shifted right one bit 
xx000100 - shifted right another bit 
xxx00010 - shifted right another bit 

与在C存在,在零偏移版本,也就是逻辑右移,在零偏移

00010010 - our original number 0x12 
00001001 - shifted right one bit 
00000100 - shifted right another bit 
00000010 - shifted right another bit 

算术右移装置保存“符号位”是什么符号位?如果没有的话,你也需要学习两个补码。基本上,如果你认为位模式/值是一个二进制补码,那么最重要的位,即左边的位是符号位。如果它是0,则数字是正数,1是负数。您可能已经注意到,左移1位的移位等于乘以2,右移与2除相同。0x12 >> 1 = 0x9,18 >> 1 = 9但是如果我们要移位一个减2到正确的一个,减2是0xFE使用字节或0b11111110。使用C风格逻辑右移0xFE >> 1 = 0x7F,或以十进制-2 >> 1 = 0x127。我们解决不了,在C在一个单一的操作,很遗憾,但在装配,我们可以使用算术移位,假设你的指令集有一个,它的手臂确实

算术右移

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 

所以,如果符号位S是0的时候我们就开始,如果该号码为01100100,然后

01100100 - our starting value 
00110010 - one shift right 
00011001 - another shift right 
00001100 - another shift right 

但如果该标志位一直是一个

11100100 - our starting value 
11110010 - one shift right 
11111001 - another shift right 
11111100 - another shift right 

我们可以解决0xFE的右边移动一个:

11111110 - 0xFE a minus 2 in twos complement for a byte 
11111111 - shifted right one 

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

您需要自行读取的最后一件事情与旋转和/或结束时偏移的位相关。右移位移到数字的“结尾”,就像块被滑动的桌子一样,掉落的块可能会进入“位桶”(以太,天堂或地狱,其中一个位当他们从这个世界消失时去死)。但是某些指令集中的某些指令会将该位置移位并将其置于进位标志(在加法和减法中进行读取),并不是因为它必须是进位,而是因为alu中的状态位和进位位是有道理的。现在旋转的是什么,可以说你有一个8位的处理器,并且你旋转了一位,位在Carry位的末端降落,AND另一端的位移是进位位手术前。基本上它是音乐椅子,有些人在椅子周围走来走去,一个人站着,站着的人是坐着的人,坐在椅子上的人是坐在椅子上的人。为什么这有用?可以说我们有一个像爱特梅尔AVR这样的8位处理器,但是想要做一个64位的转换。 64位需要8位,8位寄存器,比如说我在这8个寄存器中有64位数字,我想要做一个64位左移一位。我将从最低有效字节开始,并执行一个lsl,它将零移入,但移出的位进入进位位。那么下一个最重要的字节就是一个字节,向左旋转一位,进入的位是从先前字节出来的位,并且出去的位进入进位位。我重复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 

是什么旋转是,这就是为什么在组装手册困扰告诉你标志修改什么,当你执行逻辑运算。

+0

哇,你的回答给我留下了深刻的印象! – 2015-12-17 09:07:51

+0

这是一个非常好的答案!我们可以让管理员保存这个吗? – 71GA 2018-02-08 10:32:22

+0

@old_timer你可能知道为什么我在使用BIC语法时为Thumb编译过程中需要unshifted register - bic r0,r0,#0x3吗?这仍然是从2007年的错误? https://gcc.gnu.org/bugzilla/show_bug.cgi?id=34436 – 71GA 2018-02-08 17:17:28

3

我会做第一个,那么也许你可以尝试使用类似的方法计算出的其余部分:

/** LSL **/ 
mov r0, #1   ; r0 = 0000 0000 0000 0000 0000 0000 0000 0001 
mov r3, r0, LSL#10 ; r3 = r0 logically shifted left by 10 bit positions 
          = 0000 0000 0000 0000 0000 0100 0000 0000 
                ^  ^
                 +<<<<<<<<<<<+ 
                shift left 10 bits 

不过请注意,如果你还不明白布尔运算,如OR( (|),AND(&)等,那么您将很难理解相应的ARM指令(ORRAND等)。

相关问题