tags:

views:

1793

answers:

5

hello,

i have got an 32bit (hexadecimal)word 0xaabbccdd and have to swap the 2. and the 3. byte. in the end it should look like 0xaaccbbdd

how can i "mask" the 2nd and the 3rd byte to first load them up to register r1 and r2 and the swap them.. i also know that i have to work with lsl and lsr commands but dont know how to start.

sorry for my bad english.hope anyone could help me out!

regards, sebastian

+5  A: 

That's not a simple task in ARM assembly because you can't easily use 32 bit constants. You have to break up all your operations that mask out bytes to use 8 bit constants each (also these constants can be rotated).

You mask out byte2 and 3 using the AND instruction and do the shift later. in ARM-assembler you have with most instruction one shift for free, so the shift-into-position and merge with the other bits often end up beeing a single instruction.

Here is some untested code that does the middle byte swap (ARMv4, not thump-instruction set):

        .text

swap_v4:
        AND     R2, R0, #0x00ff0000     @ extract byte 2
        AND     R3, R0, #0x0000ff00     @ extract byte 1
        BIC     R0, R0, #0xff000000     @ clear byte 3
        BIC     R0, R0, #0x000000ff     @ clear byte 0
        ORR     R0, R2, LSR #8          @ merge and shift byte 2
        ORR     R0, R3, LSL #8          @ merge and shift byte 2
        B       LR

That translate line by line into the following c-code:

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

You'll see - lots of lines for such a simple task. Not even the ARMv6 architecture helps here much.


EDIT: ARMv6 version (also untested, but two instructions shorter)

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
Nils Pipenbrinck
Uh, isn't that using 32-bit constants, in the first example? Confusing.
unwind
Yes, these are 32 bit constants, but if you look closer they only have 8 successive bits used in their constants. ARM can do 8 bits, but the position of these can be freely rotated into the place you want. The assembler is smart enough to encode the constants for you.
Nils Pipenbrinck
A: 

hello,

thank you nils for answering my question. the problem is i am working with an arm7 and compiling your snippet with arm-elf-gcc file.S i am getting errors like print(" Error: register or shift expression expected -- r0, r2, lsr #8 "); i think its because of the arm7 version i use.

i will test it again and willl give some feedback tomorrow. thanks again!

regards, sebastian

buk
hi sebastian.most likely it's just some syntax issue with the GCC-version. I suggest that you disassemble some code (or compile with -S) and take a look how your gcc-version encodes shifts. I've seen versions that want ORR r0,r2, lsr, #8 (note the extra colon) for example.
Nils Pipenbrinck
A: 

You vould just use pointers to swap two bytes

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;

And now the result is

swap.DWbuf == 0xaaccbbdd;
eaanon01
+2  A: 

Hmmm, dont know what happened, it submitted my answer before I had really started.

At first I didnt think I could do it with only two registers but then I decided I could and did. These solutions are register only, no memory (other than the ldr r0,= which you can replace with four instructions). If you use memory and hmmm, two registers you can cut down the number of instructions perhaps, str, bic, bic, ldrb, orr lsl, ldrb, orr lsl. Okay I did it in one instruction fewer but then you need the memory location and the stores and loads cost cycles so same amount of memory and more cycles for me to do it with memory. Someone else may have some good tricks. I think some of the newer cores have an endian swap instruction which would make it even easier.

.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
dwelch
+4  A: 

Back in the day we used to rely heavily on EOR for this kind of trickery.

You can do it in 4 cycles.

First off, we need the fact that: A ^ (A^B) = B

We start with 0xAABBCCDD, and we want 0xAACCBBDD. To get there, we need 0x00EEEE00^0xAABBCCDD, where EE = BB^CC.

Now, we need a few cycles to build 00EEEE00:

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

In c:

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

After each line, the result calculated is: starting with: AABBCCDD

eor  XXXXEEXX
and  0000EE00
orr  00EEEE00
eor  AACCBBDD

This will work on any 32bit ARM core.

Dave Gamble