スーパーバイザモードで SVC を呼び出す場合は、LR と SPSR の元の値が失われないように SVC LR および SPSR を保存しておく必要があります。
SVC
命令が実行される場合、以下のようになります。
プロセッサがスーパーバイザモードに入ります。
CPSR が SVC SPSR に格納されます。
リターンアドレスが SVC LR に格納されます。
プロセッサが既にスーパーバイザモードにある場合、SVC LR と SPSR を格納していなければ破壊されます。
例えば、特定の SVC 番号のハンドラルーチンが別の SVC を呼び出す場合は、そのハンドラルーチンで SVC LR と SPSR の両方をスタックに格納する必要があります。この動作の結果、ハンドラが呼び出されるごとに、ハンドラを呼び出した SVC
に続く命令に復帰するために必要な情報が保存されることが保証されます。以下の例は、この方法を示しています。
AREA SVC_Area, CODE, READONLY PRESERVE8 EXPORT SVC_Handler IMPORT C_SVC_Handler T_bit EQU 0x20 SVC_Handler PUSH {R0-R3,R12,lr} ; レジスタを保存する。MOV R1, sp ; ポインタをパラメータに設定する。MRS R0, SPSR ; SPSR を取得する。PUSH {R0,R3} ; SPSR をスタックおよび別の ; レジスタを保存して、 ; 8 バイトで整列されたスタックを保持する。; 入れ子になった SVC でのみ必要。TST R0,#0x20 ; Thumb ステートで発生したか?LDRHNE R0,[lr,#-2] ; はい:ハーフワードをロードし...BICNE R0,R0,#0xFF00 ; ...コメントフィールドを抽出する。LDREQ R0,[lr,#-4] ; いいえ:ワードをロードし...BICEQ R0,R0,#0xFF000000 ; ...コメントフィールドを抽出する。; R0 は SVC 番号を含む ; R1 はスタックレジスタへのポインタを含む ; BL C_SVC_Handler ; SVC を処理するための C ルーチンを呼び出す。POP {R0,R3} ; スタックから SPSR を取得する。MSR SPSR_cf, R0 ; SPSR を復元する。LDM sp!, {R0-R3,R12,pc}^ ; レジスタを復元し、復帰する。END
C または C++ で入れ子になった SVC を記述できます。コンパイラによって生成されるコードは、必要に応じて lr_SVC
のストアと再ロードを実行します。