Jun's Homepage

玄箱PRO、Linux ZaurusでARMアセンブリプログラミング

copyright (C) 2003-2009 Jun Mizutani

6. システムコールとその他の命令

これまで演算命令、分岐命令、 ロード/ストア命令、複数レジスタのロード/ストア命令を見てきましたが、 今回はそれ以外の命令の概要です。まず、非常に重要な Linuxのシステムコールを呼び出すために使用する SVC命令(SWI から名前が変更されました)から始めましょう。

ソフトウェア割り込み命令

ソフトウェア割り込み命令は、Linux ではカーネルのシステムコールの呼び出しに使用されます。 SVC が実行されるとスーパーバイザモード(カーネルモード)に移行し、CPSR は SPSR_svc に待避され、 プログラムカウンタ(PC) に メモリアドレス 0x00000008 に保存されたSVC 例外ベクタ (例外処理ルーチンの先頭アドレス) がロードされます。
「SVC命令を実行するとカーネルのシステムコール処理ルーチンに制御が渡る」と覚えておきましょう。

オペコード オペランド 動作 意味
SVC <24bit_number> PC=mem[0x00000008]
SPSR_svc = CPSR
ソフトウェア割り込み

SVC命令の、下位24ビットは無視され、SVC命令の実行に影響しませんが、 Linux(OABI)ではシステムコール番号 ( に 0x900000 を加えたもの ) を指定します。 カーネルの割り込みハンドラ(システムコール処理ルーチン)側でSVC命令自体を読み出して システムコール番号を抽出しています。その後、システムコールのジャンプテーブルから 特定のシステムコールに制御を渡します。 興味のある方はカーネルソース /usr/src/linux/arch/arm/kernel/entry-common.S の ENTRY(vector_swi) から正確に書いてありますので参考にして下さい。 (^-^)

アセンブラから Linux のシステムコールを使う場合は、 システムコールの引数を r0 レジスタから順 (最大 r6 まで、現在のカーネルでは r5 まで) に値を設定して、 SVC のオペランドにシステムコール番号 (に 0x900000 を加えたもの) を指定することで システムコールを呼び出すことができます。システムコールの返り値は r0 に設定されます。

たとえば、プログラムを終了させるシステムコール番号1番のシステムコールを実行するには、

       mov    r0, #0
       swi    #0x900001    @ sys_exit

のようにして呼び出します。簡単ですね。

玄箱PRO や Debian のarmel など EABIのLinuxではシステムコール番号1番のシステムコールを実行するには、

       mov    r0, #0
       mov    r7, #1       @ sys_exit
       swi    #0

のように r7 レジスタにシステムコール番号を設定して呼び出します。SVC のオペランドは 0 とします。

システムコールの種類、番号、引数の一覧はこちらを参照してください。 個々のシステムコールの使用方法は今後解説する予定です。

乗算命令

積算命令 (MUL) は32ビットの乗算を行います。積和命令 (MLA) は乗算と加算を同時に 行う命令です。32ビット同士の積算で結果も32ビットであるため、 下位32ビットは符号付き(2の補数)、符号無しのどちらの場合も同じになります。

倍精度積算(multiply long)命令は、32ビットの整数積算を行い、64ビットの結果を返します。積算と積和(64ビット整数の加算も行う)、符号付きと符合無し演算があり、計4種類の命令(UMULL,UMULAL,SMULL,SMULAL)があります。

オペコード オペランド 動作 意味
MLA<cd><S> Rd, Rm, Rs, Rn Rd = Rm * Rs + Rn 積和(32=32*32+32)
MUL<cd><S> Rd, Rm, Rs Rd = Rm * Rs 乗算(32=32*32)
SMLAL<cd><S> RLo, RHi, Rm, Rs (RHi, RLo) = Rm * Rs + (RHi, RLo) 符号付積和(64=32*32+64)
SMULL<cd><S> RLo, RHi, Rm, Rs (RHi, RLo) = Rm * Rs 符号付乗算(64=32*32)
UMLAL<cd><S> RLo, RHi, Rm, Rs (RHi, RLo) = Rm * Rs + (RHi, RLo) 積和(64=32*32+64)
UMULL<cd><S> RLo, RHi, Rm, Rs (RHi, RLo) = Rm * Rs 乗算(64=32*32)

積算(UMULL, SMULL)命令では2つの32ビット整数から64bitの積算結果を得ます。 結果の下位32ビットが RdLoに、上位32ビットが RdHi に代入されます。 積和算(UMLAL, SMLAL)命令では2つの32ビット整数の積を計算し、その結果に64ビット整数を加えて 64ビットの結果を得ます。 ここで加算される64ビット整数、および結果の64ビット整数はどちらも 下位32ビットとして RdLoが、上位32ビットとして RdHiが使用されます。

UMULL と UMLALは全てのオペランドを符号無しとみなして演算を行い、結果も符号無しで返ります。 SMULL と SMLAL ではオペランドは2の補数形式の符号付きと見なされ、 結果は2の補数形式の符号付きで返ります。

SMLAL(符号付倍精度積和演算)命令は、固定小数点形式の行列の乗算に使うと便利そうです。

ステータスレジスタの操作

MRSおよびMSR命令はステータスレジスタ(PSR : Program Status Register)と汎用レジスタ間の読み書きを行う命令です。 MRS命令はPSRの内容を汎用レジスタへ読み出し、MSR命令は汎用レジスタの内容をPSRへ書き込みます。 またMRS命令では、定数 (イミディエイト) または汎用レジスタの内容を、制御ビットに影響を 与えることなくPSRの条件フラグ(N,Z,C,V)に転送することができます。 指定した値の上位4ビットがPSRに転送されます。

オペコード オペランド 動作 意味
MRS<cd> Rd, CPSR Rd=CPSR
MRS<cd> Rd, SPSR Rd=SPSR
MSR<cd> CPSR_<fields>, Rm CPSR = field_mask( Rm )
MSR<cd> CPSR_<fields>, #8bit CPSR = field_mask( #8ビット定数 )
MSR<cd> SPSR_<fields>, Rm SPSR = field_mask( Rm )
MSR<cd> SPSR_<fields>, #8bit SPSR = field_mask( #8ビット定数 )

8ビット定数の部分は8ビットの値をローテイトして生成できる値を指定することができます。 例えば、#0xF000000 や #0x0770000 が可能です。ローテイトするビット数はアセンブラが 自動的に決定してくれます。

<fields> は次のうちの1つ以上を指定できますが、f 以外は使う必要がないと思います。

c 制御フィールドマスク (PSR[7:0])
x 拡張フィールドマスク (PSR[15:8])
s 状態フィールドマスク (PSR[23:16)
f フラグフィールドマスク (PSR[31:24])

PSR には CPSR (Current Program Status Register) と SPSR (Saved Program Status Register) がありますが、 通常のプログラム (ユーザモード) では CPSR だけを使います。

スワップ命令

SWP命令はレジスタとメモリ間で32ビットまたは8ビットのデータを交換します。 レジスタとメモリ間でデータを交換するためには、メモリからレジスタへのの読み出しと レジスタからメモリへの書き込みを行う必要がありますが、割り込みされない状態で これを1命令で実行します。

オペコード オペランド 動作 意味
SWP<cd> Rd, Rm, [Rn] Rd=[Rn]; [Rn]=Rm 交換(32bit)
SWP<cd>B Rd, Rm, [Rn] Rd=[Rn]; [Rn]=Rm 交換(8bit)

最初にアドレス Rn の内容を読み出し、次にレジスタ Rm の内容をそのアドレスに書き出します。 そして前に読み出しておいたメモリの内容を Rd に格納します。

この命令は排他制御のためのセマフォの実装に使うようです。
セマフォの取得は:

  adr    r1, semaphore   @ セマフォのメモリアドレス
  mov    r0, #0
  swp    r0, r0, [r1]    @ セマフォを取得(全部)
  cmp    r0, #0
  subne  r0, r0, #1
  swpne  r0, r0, [r1]    @ 残りを返す(セマフォの値 - 1)

セマフォを返す場合は:

  adr    r1, semaphore   @ セマフォのメモリアドレス
  mov    r0, #0
  swp    r0, r0, [r1]    @ セマフォを取得(全部)
  add    r0, r0, #1
  swp    r0, r0, [r1]    @ セマフォを返す(セマフォの値 + 1)

という感じでしょうか? セマフォの最大値が 1 の場合なら:

  adr    r1, semaphore   @ セマフォのメモリアドレス
  mov    r0, #0
  swp    r0, r0, [r1]    @ セマフォを取得
  cmp    r0, #0

セマフォを返す場合は:

  adr    r1, semaphore   @ セマフォのメモリアドレス
  mov    r0, #1
  swp    r0, r0, [r1]    @ セマフォを返す

という感じになると思います。この辺は自信ありません。

その他の命令

CLZ命令はRmレジスタの値に関して、最上位にある0ビットの数をRdレジスタに返します。 除算ルーチンで使える命令です。

オペコード オペランド 動作 意味
CLZ{cd} Rd, RmRd = NoOfZero( Rm ) Count Leading Zeros

XScale では、そのほかにもブレークポイント命令(BKPT)、THUMBモード命令、コプロセッサ操作命令 (CDP, LDC, STC, MCR, MRC)、内部アキュムレータ操作命令(MAR, MRA, MIA)、飽和演算の機能を持つ Enhanced DSP 操作命令などを使用できますが、もし使用することがあれば、その都度解説することにします。

Jun's Homepage

  • HOME に戻る
  • 前のページ
  • 次のページ
  • webg:WebGPU 3Dライブラリ
  • Blender
  • Raspberry Pi メモ
  • rvtl:Tiny Basic
  • ARM64 アセンブリ
  • ARM アセンブリ
  • x86 アセンブリ
  • x86-64 アセンブリ
  • PowerPC アセンブリ

ARM アセンブリ

  • はじめに
  • 1. アセンブラのインストールと実行
  • 2. ARMのレジスタ
  • 3. 演算命令と分岐命令
  • 4. ロード/ストア命令
  • 5. ロード/ストア命令(複数レジスタ)
  • 6. システムコールとその他の命令
  • 7. アセンブラGNU asの基礎知識
  • 8. 標準入出力用のサブルーチンの作成
  • 9. フレームバッファでグラフィックス - その1
  • A. システムコールと引数一覧
  • B. インタプリタの作成

Raspberry Pi メモ

  • Raspberry Pi メモ 目次
  • ★ 逆引き Raspberry Pi メモ ★
  • (1) Raspberry Pi到着
  • (2) ブート用SDカードの作成
  • (3) アセンブリで浮動小数点数表示
  • (4) 軽く三次元
  • (5) ベクトル演算の実力
  • (6) 転んでも泣かない!
  • (7) OpenGL ES2 (1) 概要
  • (8) OpenGL ES2 (2) 初期化
  • (9) 公式オーバークロック
  • (10) OpenGL ES2 (3) シェーダの使い方
  • (11) OpenGL ES2 (4) 三次元で三角形
  • (12) OpenGL ES2 (5) テクスチャ
  • (13) OpenGL ES2 (6) シェーダで照明
  • (14) 新wheezy-raspbian と「まとめ」
  • (15) Tiny BASIC と ASLR
  • (16) 新イメージ 2012-12-16
  • (17) 各種言語のベンチマーク
  • (18) Pi専用の Minecraft の使い方
  • (19) LuaJITでお手軽3D (1)
  • (20) LuaJITでお手軽3D (2) LjESリリース
  • (21) 新 raspbian と Wayland/Weston
  • (22) LuaJITでお手軽3D (3) NodeとShape
  • (23) カメラモジュールの使い方
  • (24) 2013-09-25-wheezy-raspbian と「まとめ」
  • (25) LuaJITでお手軽3D (4) テクスチャマッピング
  • (26) raspbian(2013-12-20)とMathematica
  • (27) RPI Expansion Board (X100)のレビュー
  • (28) 液晶ディスプレイ一体型PC
  • (29) LuaJITでお手軽3D (5) LjES-2 リリース
  • (30) GPIO とシャットダウンボタン
  • (31) Lua でオブジェクト指向
  • (32) 新Raspbianとリアルタイムクロック
  • (33) 2014-09-09-wheezy-raspbian のインストール
  • (34) デスクトップの軽量な日本語化
  • (35) デスクトップのカスタマイズ
  • (36) 2014-12-24-wheezy-raspbian の変更点
  • (37) Java8 プログラミング
  • (38) Raspberry Pi でラムダ式
  • (39) Raspberry Pi 2 始動
  • (40) Raspberry Pi2 の互換性
  • (41) ゲームパッドでシャットダウン
  • (42) Raspberry Pi2 の非互換性とGPIO
  • (43) Node.js で JavaScript
  • (44) ハードディスク起動とヘアピンNAT
  • (45) 2015-05-05版と新しい無線LAN設定方法

アセンブリ言語シリーズ

  • PowerPC アセンブリ
  • ARM アセンブリ
  • ARM64 アセンブリ
  • x86 アセンブリ
  • x86-64 アセンブリ

ARM64 アセンブリ

  • 0. はじめに
  • 1. メモリ、バイト、エンディアン
  • 2. アセンブラのインストール
  • 3. ARM64 のレジスタ
  • 4. ロード命令
  • 5. ストア命令
  • 6. 演算命令
  • 7. シフト命令
  • 8. 条件分岐、システムコール
  • 9. 浮動小数点レジスタと命令
  • 10. 浮動小数点数の表示
  • 11. 浮動小数点数のロード/ストア命令
  • 12. ベクトルのロード/ストア命令
  • 13. 浮動小数点数のレジスタ間移動命令
  • つづく...

  • インタプリタの作成
  • Dragonboard 410C のインストール
  • 仮想 Arm64 マシンの作り方
  • HiKey のインストール
  • Orange Pi PC2 の性能と Ubuntu のインストール
  • Raspberry Pi2、Orange Pi Zero/PC2 の UnixBench

アセンブリ言語シリーズ

  • PowerPC アセンブリ
  • ARM アセンブリ
  • x86 アセンブリ
  • x86-64 アセンブリ

ご意見・ご感想は,こちらの掲示板まで。

78932