やまものブログ

メモ書きブログです (^_^;A

ARM アセンブラ operand2

Cortex-A Series プログラマーズガイド (DEN0013C) を精読せずいい加減に読んでると、最後のオペランド (つまり operand2) はバリエーションがたくさんあって手強いという印象を持ちました。

たとえば、以下のようなオペランドが例として記載されていました。
add R0, R1, R2, LSL #4
stmfd sp!, {fp, ip, lr, pc}
ldmfd sp, {fp, sp, pc}
MSR CPSR_c, #Mode_FIQ:OR:I_Bit:OR:F_Bit

最初の例にある R2, LSL #4  は
6.2.1 Operand 2 and the barrel shifter
を落ち着いて読めばちゃんと解説されていました。

次の2つ、stmfd と ldmfd は Load/Store Multiple なるもので、 { } でくくられたリストはロードまたはストアの対象となるレジスタで複数並べられます。ちなみに、ロード・ストアされる順番は、並び順ではなくて、レジスタ番号に従った順序だそうです。

最後の例は MSR (Move Status Register or Coprocessor Register from General Purpose Register) の命令仕様と CPSR (Current Program Status Register)のビット定義をじっくり見比べるとやりたいことは理解できます。ここで Mode_FIQ、I_BIT、F_BIT は EQU ディレクティブ (C言語の #define マクロ?) で置き換えられた定数と思われますが、Linuxソースコード(linux-xlnx.git) を探しても見当たりませんでした。あと、:OR: は ARMアセンブラの場合で、GNUの場合は になります。以上を確認するため、下記のコードをコンパイル・リンクできることを確認しました。実行しても何も起りませんが
 
int main(int argc, char *argv)
{
        asm volatile(" \
        .equ    Mode_FIQ,    0x11 ; \
        .equ    I_BIT    ,    0x80 ; \
        .equ    F_BIT    ,    0x40 ; \
        MSR CPSR_c, #Mode_FIQ | I_BIT | F_BIT ; \
        ");
        return 0;
}



前後しますが、最初の例の LSL (left shift) を含むサンプルも実行してみました。

#include <stdio.h>

int main(int argc, char *argv)
{
        register unsigned int v4;
        register unsigned int v5;
        register unsigned int v6;

        asm volatile(" \
       
        MOVW %0, #0xdead; \
       
        MOVW %1, #0xbeaf; \
       
        ADD %2, %1, %0, LSL #16; \
       
        " : "=r"(v4), "=r"(v5), "=r"(v6));
        printf("%x\t%x\t%x\n", v4, v5, v6);
        return 0;
}


これは下記のようのに結果が確認できます

root@zynq:~# ./a.out
dead    beaf    deadbeaf