3

私はこのフォーラムで多くのトピックを読み、このテーマに関する多くの回答を見つけました。アセンブリコードからC関数に5つの引数を渡すことができました。これを行うために、私は以下の手順を使用しました:

mov r0, #0
mov r1, #1
mov r2, #2
mov r3, #3
mov r4, #4
STR r4, [sp, #-4]!
BL displayRegistersValue

しかし、今日、私はレジスター全体をC関数に渡して、それらをC構造体に保存しようとしています。私はこの指示で試しました:

STMDB sp!, {registers that i want to save}

私のC関数:

displayRegistersValue(int registers[number_of_registers])
char printable = registers[0] + (int)'0'; // Convert in a printable character
print_uart0(&printable);

しかし、私の表示は良くありません。では、Cコードのレジスタにアクセスするにはどうすればよいですか?

4

4 に答える 4

5

ARM標準では、R0〜R3のみが値で渡されるため、最大4つしか許可されていないことを確認してください。さらに値が必要な場合は、コンパイラのように、それらをスタックにプッシュして、その方法でアクセスします。または、構造体を作成してそのアドレスを渡します。

わかりました。2倍になりました。ここに、ARMの呼び出し規約へのリンクがあります。ページを少し下に移動します。

必要なことを行うには、メモリ位置(配列)のアドレスをアセンブリルーチンに渡します。おそらくr0内でそのアドレスを取得したら、stmdbを実行できます。その場所にすべてのレジスタ値とそのメモリがCレベルで表示可能になります。

注意してください、これはおそらくあなたが思うことをするつもりはありません。上記の呼び出し規約のリンクに従って、これらの値はかなり変更できます。これがデバッグ用である場合は、デバッガーを使用してレジスターをそのように監視することをお勧めします。

わかりました、あなたはまだここで理解していません:


{
    int registerValues[14];

    myAsmRoutine(registerValues);

    print_uart0(& registerValues);
}

myAsmRoutine:
    stmia  r0!, {r1-r14}
    blx   lr

R0とPCをスキップしましたが、わかります。また、値を印刷可能な形式(sprintfまたはitoa osなど)に変更するには、少し複雑な操作を行う必要があります。

于 2013-02-12T16:32:41.917 に答える
2
displayRegistersValue(int registers[number_of_registers])

これは構造体ではなく配列であり、アイテムの長いリストではなく、何かへのポインタとして渡されます。構造物についても同じことが言えます。

通常、asmで必要なことを実行するC関数を作成し、コンパイラーが生成するものを確認して、そこから実行するのが最も簡単です(ABIドキュメントを使用して確認するなど)。

#define NUMREGS 13
void displayRegistersValue(unsigned int registers[NUMREGS]);
void outer ( void )
{
    unsigned int regs[NUMREGS];
    displayRegistersValue(regs);
}

> arm-none-linux-gnueabi-gcc -O2 -c fun.c -o fun.o
> arm-none-linux-gnueabi-objdump -D fun.o

fun.o:     file format elf32-littlearm


Disassembly of section .text:

00000000 <outer>:
   0:   e52de004    push    {lr}        ; (str lr, [sp, #-4]!)
   4:   e24dd03c    sub sp, sp, #60 ; 0x3c
   8:   e28d0004    add r0, sp, #4
   c:   ebfffffe    bl  0 <displayRegistersValue>
  10:   e28dd03c    add sp, sp, #60 ; 0x3c
  14:   e49df004    pop {pc}        ; (ldr pc, [sp], #4)

同様のことを行う必要があります。スタックポインタに追加してスタックにスペースを確保し、lrを保存して、分岐リンクでゴミ箱に入れないようにします。レジスタをそのメモリ(スタック)ポイントr0の先頭にコピーします。渡したいメモリ/配列を呼び出してから、関数を呼び出します(r0は、関数に渡す最初で唯一のパラメータです)。

push {lr} 
mov lr,sp 
stmdb sp!,{r0-r12} 
mov r0,lr 
bl displayRegistersValue
add sp,sp,#52
pop {lr}
于 2013-02-12T20:13:03.110 に答える
0

配列は、単一のレジスタでポインタとして渡されます。5つのレジスタが必要な場合は、5つのパラメータ(int i1、int i2など)が必要です。

于 2013-02-12T16:24:44.273 に答える
0

ARM APCSドキュメントから引用するには:

「最初の4つのレジスタr0-r3(a1-a4)は、引数値をサブルーチンに渡し、関数から結果値を返すために使用されます。また、ルーチン内で中間値を保持するためにも使用できます(ただし、一般的には、サブルーチン呼び出し間のみ)。」

したがって、4つを超える値をC関数に渡す場合は、残りの値をスタックに渡す必要があります。より良いアイデアは、静的に割り当てられたメモリ領域にレジスタ値を配置し、メモリのアドレス(ポインタ)をC関数に渡すことです。関数によってポインタを逆参照して、レジスタ値を取得できます。

于 2013-02-12T16:44:28.113 に答える