如何在ARM程序集中屏蔽字节?

时间:2008-12-07 18:38:15

标签: assembly arm

我有一个32位(十六进制)字0xaabbccdd并且必须交换2.和3.字节。最后它应该看起来像0xaaccbbdd

如何“屏蔽”第2个和第3个字节,首先将它们加载到寄存器r1和r2并交换它们。 我也知道我必须使用lsl和lsr命令,但不知道如何开始。

抱歉我的英语不好。任何人都可以帮助我!

的问候, 塞巴斯蒂安

5 个答案:

答案 0 :(得分:8)

在我们过去非常依赖EOR的那一天,这种诡计。

你可以在4个周期内完成。

首先,我们需要以下事实:A ^(A ^ B)= B

我们从0xAABBCCDD开始,我们想要0xAACCBBDD。 为此,我们需要0x00EEEE00 ^ 0xAABBCCDD, 其中EE = BB ^ CC。

现在,我们需要几个周期来构建00EEEE00:

eor     r1,r0,r0,lsr #8
and     r1,r1,#0xFF00
orr     r1,r1,r1,lsl #8
eor     r0,r0,r1

在c:

t=x^(x>>8);
t=t&0xFF00;
t=t|(t<<8);
x^=t;

在每一行之后,计算的结果是: 从:AABBCCDD开始

eor  XXXXEEXX
and  0000EE00
orr  00EEEE00
eor  AACCBBDD

这适用于任何32位ARM内核。

答案 1 :(得分:6)

这不是ARM程序集中的简单任务,因为您无法轻松使用32位常量。你必须分解所有掩盖字节的操作,每个操作使用8位常量(也可以旋转这些常量)。

使用AND指令屏蔽byte2和3并稍后进行移位。在ARM-assembler中你可以免费获得大多数指令一个移位,因此移位到位并与其他位合并通常最终只能作为一条指令。

这是一些未经测试的代码,它执行中间字节交换(ARMv4,而不是拇指指令集):

        .text

swap_v4:
        AND     R2, R0, #0x00ff0000     @ R2=0x00BB0000 get byte 2
        AND     R3, R0, #0x0000ff00     @ R3=0x0000CC00 get byte 1
        BIC     R0, R0, #0x00ff0000     @ R0=0xAA00CCDD clear byte 2
        BIC     R0, R0, #0x0000ff00     @ R0=0xAA0000DD clear byte 1
        ORR     R0, R2, LSR #8          @ R0=0xAA00BBDD merge and shift byte 2
        ORR     R0, R3, LSL #8          @ R0=0xAACCBBDD merge and shift byte 1
        B       LR

逐行转换为以下c代码:

int swap (int R0)
{
  int R2,R3;
  R2 = R0 & 0x00ff0000;
  R3 = R0 & 0x0000ff00;
  R0 = R0 & 0xff00ffff;
  R0 = R0 & 0xffff00ff;
  R0 |= (R2>>8);
  R0 |= (R3<<8);
  return R0;
}

你会看到 - 这么简单的任务有很多行。甚至ARMv6架构也没有帮助。


编辑:ARMv6版本(也未经测试,但两条指令更短)

swap_v6:
        @ bits in R0: aabbccdd
        ROR     R0, R0, #8              @ r0 = ddaabbcc
        REV     R1, R0                  @ r1 = ccbbaadd
        PKHTB   R0, R0, R1              @ r0 = ddaaccbb
        ROR     R0, R0, #24             @ r0 = aaccbbdd
        BX      LR

答案 2 :(得分:2)

嗯,不知道发生了什么事,它在我真正开始之前提交了我的答案。

起初我并不认为只用两个寄存器就能做到这一点,但后来我决定能做到。这些解决方案只是寄存器,没有内存(除了ldr r0,=可以用四条指令替换)。如果你使用内存和嗯,两个寄存器,你可以减少指令的数量,str,bic,bic,ldrb,orr lsl,ldrb,orr lsl。好吧,我在一个指令中做了更少,但是你需要内存位置和存储并加载成本周期,所以内存量和内存更多周期。其他人可能有一些好的技巧。我认为一些较新的内核有一个endian交换指令,这将使它更容易。

.globl midswap
midswap:
    mov r2,r0,lsl #8      ;@ r2 = BBCCDDAA
    mov r3,r0,lsr #8      ;@ r3 = DDAABBCC (this might drag a sign bit, dont care)
    and r2,r2,#0x00FF0000 ;@ r2 = 00CC0000
    and r3,r3,#0x0000FF00 ;@ r3 = 0000BB00
    bic r0,r0,#0x00FF0000 ;@ r0 = AA00CCDD
    bic r0,r0,#0x0000FF00 ;@ r0 = AA0000DD
    orr r0,r0,r2          ;@ r0 = AACC00DD
    orr r0,r0,r3          ;@ r0 = AACCBBDD
    bx lr ;@ or mov pc,lr for older arm cores


.globl tworegs
tworegs:
    mov r2,r0,ror #8       ;@ r2 = DDAABBCC
    bic r2,r2,#0xFF000000  ;@ r2 = 00AABBCC
    bic r2,r2,#0x00FF0000  ;@ r2 = 0000BBCC
    orr r2,r2,ror #16      ;@ r2 = BBCCBBCC
    bic r2,r2,#0xFF000000  ;@ r2 = 00CCBBCC
    bic r2,r2,#0x000000FF  ;@ r2 = 00CCBB00
    bic r0,r0,#0x00FF0000  ;@ r0 = AA00CCDD
    bic r0,r0,#0x0000FF00  ;@ r0 = AA0000DD
    orr r0,r0,r2           ;@ r0 = AACCBBDD
    bx lr

testfun:
    ldr r0,=0xAABBCCDD
    bl midswap

答案 3 :(得分:1)

您可以使用BFI和UBFX,这将使您的工作更轻松

答案 4 :(得分:0)

您可以使用指针交换两个字节

static union {
 BYTE   BBuf[4];
 WORD   WWBuf[2];
 DWORD  DWBuf;
}swap;

unsigned char *a;
unsigned char *b;
swap.DWBuf = 0xaabbccdd;

a = &swap.BBuf[1];
b = &swap.BBuf[2];

*a ^= *b;
*b ^= *a;
*a ^= *b;

现在结果是

swap.DWbuf == 0xaaccbbdd;