私は 32 ビット (16 進数) ワード 0xaabbccdd を取得しており、2 バイトと 3 バイトを交換する必要があります。最終的には 0xaaccbbdd のようになります
2 番目と 3 番目のバイトを「マスク」して、最初に r1 と r2 を登録し、それらをスワップするようにするにはどうすればよいですか?
私の悪い英語で申し訳ありません.誰かが私を助けてくれることを願っています!
よろしく、セバスチャン
当時、私たちはこの種のトリックを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コアで機能します。
32 ビット定数は簡単に使用できないため、ARM アセンブリでは簡単な作業ではありません。それぞれ 8 ビットの定数を使用するためにバイトをマスクするすべての操作を分割する必要があります (これらの定数もローテーションできます)。
AND 命令を使用してバイト 2 と 3 をマスクし、後でシフトを実行します。ARM アセンブラーでは、ほとんどの命令で 1 つのシフトを無料で使用できるため、位置へのシフトと他のビットとのマージは、多くの場合、単一の命令になります。
以下は、ミドル バイト スワップを実行する未テストのコードです (サム命令セットではなく 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
これは、1 行ずつ次の 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 アーキテクチャでさえ、ここではあまり役に立ちません。
EDIT:ARMv6バージョン(これもテストされていませんが、2命令短いです)
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つのレジスタだけでできるとは思っていませんでしたが、それからできると思いました。これらのソリューションはレジスタのみであり、メモリはありません(ldr r0、= 4つの命令で置き換えることができます)。メモリとhmmm、2つのレジスタを使用する場合、おそらく命令の数を減らすことができます。str、bic、bic、ldrb、orr lsl、ldrb、orrlslです。さて、私は1つの命令でそれを行いましたが、メモリの場所が必要であり、保存とロードのコストサイクルが必要なので、同じ量のメモリとより多くのサイクルでメモリを使用できます。他の誰かがいくつかの良いトリックを持っているかもしれません。新しいコアの中には、エンディアンスワップ命令があり、それがさらに簡単になると思います。
.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
BFI と UBFX を使用できますか?
ポインタを使用して2バイトを交換するだけです
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;