ARM命令シミュレータ army


概要

演習で使用することを目的として作られた、 ARM命令セットを解釈・実行するソフトウェアシミュレータである。
各命令を5サイクルで処理するように作られている。

シミュレータはC++言語により記述されており、構造が単純であるため全体の 構造を短期間に理解可能である。 そのため、シミュレータの改造や機能拡張が容易である。

C言語あるいはARMアセンブリ言語で記述したプログラムソースファイルを クロスコンパイラおよびクロスアセンブラで実行可能バイナリコードを生成し、 生成したバイナリコードを16進表現でダンプしたテキストファイル(正式には 「モトローラSレコード形式」)をシミュレータの入力として読み込み、実行する。

(現在のところ)ライブラリが要求するシステムコールを実装していないため、 printf()などの標準Cライブラリは使用できないが、 入出力の代替手段として簡単なサービスコール機能を備えており、これにより 整数型データや文字列データなどの入出力やプログラムの終了などを行うことが できる。

ソースファイル

armyは以下のファイルから構成される。

上記ソースファイルをまとめたZIPファイルを ここに置いておくので 一括してファイルを取得した場合に利用するとよい。

※Makefileを同梱しているので、 cygwin環境では makeを実行すればシミュレータをビルドできる。

命令セット

ARMv4命令セットのサブセットとなる命令に対応している。
演習での使用を目的として、必要最小限の数の命令に対応するため、 整数系の命令(の一部)のみが実装されている。
(浮動小数演算命令・SIMD演算命令は実装されていない)
具体的には以下に示す命令を実装している。
※ARMでは除算命令がないことに注意する。

参考資料
各命令の動作内容の詳細については ARMアーキテクチャリファレンスマニュアル(日本語版)あるいは ARM7TDMI Data Sheet Chapter 4を参照。

サービスコール

SWI命令を使ったサービスコールを行うことで、 データの入出力やプログラムの終了などの機能を実現することができる。
SWI命令内の定数フィールドに機能コードをセットし、レジスタ0番〜3番 (ただし、必要な個数のレジスタのみ使用)に引数をセットして SWI命令を実行する。 サービスコールの返値はレジスタ0番にセットされる。

現在、以下の機能を使うことができる。

具体的な動作はarmyソースコードの armクラスの メンバ関数do_swi()内を調べるとよい。

以下はサービスコールの使い方を示した例である。

li      $4,123        /* 表示する値をレジスタ4番にセット */
li      $2,0x21       /* 整数型データ出力の機能コード(== 0x21)を
                         レジスタ2番にセット */
break                 /* break命令を実行
                         レジスタ4番にセットした値が画面に表示される */


la      $4,SBUF_LABEL /* 文字列入力用のバッファの先頭アドレスを
                         レジスタ4番にセット */
li      $2,0x13       /* 文字列データ入力の機能コード(== 0x13)を
                         レジスタ2番にセット */
break                 /* break命令を実行
                         レジスタ4番で指定したアドレスに文字列データが
                         読み込まれる */

インラインアセンブラを使うことで、 C言語からもサービスコールを使うことができる。

int v = 123;
char strbuf[2048];

... (中略) ...

/* 整数型データ出力 */
asm ("move $4,%0" : : "r" (v));
asm ("li	$2,0x21");
asm ("break");

/* 文字列データ入力 */
asm ("move $4,%0" : : "r" (strbuf));
asm ("li	$2,0x13");
asm ("break");

C言語でインラインアセンブラ記述が続くとソースコードが読みにくくなる& 間違ったコードが入りやすくなるため、C言語のマクロとして定義しておくと 便利である。
ここにマクロ定義したヘッダファイル army.hが置いてあるので自由に使ってよい。
このヘッダファイルを使ってサービスコールを使った例を以下に示す。

#include "army.h"

int main()
{
    int v;
    char c;
    char tmpbuf[512];

    PRINT_INT(123456789);
    PRINT_CHAR('X');
    PRINT_STRING("FIZZ BUZZ");

    READ_INT(v);
    READ_CHAR(c);
    READ_STRING(tmpbuf);

    PRINT_INT(v);
    PRINT_CHAR(c);
    PRINT_STRING(tmpbuf);

    EXIT(0);

    /* control does not reach here */
    return 0;
}

C言語を使ったarmy用プログラムの記述

システムコールが実装されていないため、現時点では printf()などの標準Cライブラリ関数を使うことは出来ない。 そのため、これらの関数を使わずに記述する必要がある。
また、通常のCプログラムではmain関数の最後で特にプログラムの終了 を指示しなくても問題はなかったが、army用のプログラムでは プログラム終了を明示的に記述する必要がある。
以下はプログラム記述例(階乗の計算)である。
※以下の例ではarmy.hを 使用してプログラムの記述を省力化している。 もし自分で作るプログラムでarmy.hを 使用する場合は、プログラムのソースファイルと同じフォルダに army.hを忘れずに置くこと。

#include "army.h"

int fact(int n)
{
    if (n <= 1)
        return 1;
    else
        return n * fact(n - 1);
}

int main()
{
    int i;
    for (i = 0; i < 10; i++) {
	int v = fact(i);
        PRINT_INT(v);
    }

    EXIT(0);            /* program ends here */

    /* control never reaches here */
    return 0;
}
Cソースファイル/ 逆アセンブルリスト/ ダンプテキストファイル

ソースプログラムの記述には自分の使い慣れたテキストエディタを使えばよい。 (例えば、 TeraPadなど)

armyでのプログラムの実行方法

ARMアーキテクチャ用クロスコンパイラ/アセンブラ環境で作成した ダンプテキストファイル(.srecファイル)をシミュレータに入力して実行する。 ダンプテキストファイル(.srecファイル)のファイル名をコマンドライン引数で 指定する。
./army program_file_name.srec

例: ./army fact.srec


文責:大津