前々回あたり、uLispは周辺回路レジスタ(実際にはメモリも可)へのアクセスを許してくれると活用?してみました。しかしそれだけではありませぬ。uLispはアセンブラをも備えており、uLisp関数をアセンブリ言語命令で記述して呼び出すことも可能です。事実上、何でもアリ?強力すぎる?アセンブラ書きのお楽しみが増えた?
※Lispと一緒 投稿順 index はこちら
※実機確認は Raspberry Pi Pico2で行ってます。
※使用させていただいとります uLisp のバージョンは 4.6b (Arm用)です。
uLispのアセンブラ
uLispはアセンブラで書かれた「Lisp」関数を呼び出す機能をもっています。その構造は2段構え?です。
-
- defcode スペシャルフォームにより、バイナリ定義された「Lisp」関数を定義しすることができる。defcodeで定義された「Lisp」関数は通常のLisp関数と同様に呼び出して実行できる。
- uLispは、uLisp自身で記述されたアセンブラを持っており、Arm用のアセンブラを処理系にロードすることでアセンブリ言語命令の記述に必要なニーモニックやサポート関数などを使えるようになる。これを使用することで defcode内でバイナリでなく、ニーモニック表記のアセンブリ言語記述が可能となる。
uLispはArmだけでなくAVRでも実行可能なので、AVR向けのアセンブラもあるようですが、本体処理系に漏れなくついてくるのは1の defcode のみじゃないかとおもいます。アセンブラ部分はオプションと。
Arm用の御本家解説ページが以下に。
処理系にロードして使うアセンブラのuLispコードの在処が以下に。
手元の処理系では、上記のアセンブラのロードにより約2000ノードくらい消費されとるみたいです。アセンブルした後のバイナリコードを使ってdefcodeしてやるひと手間入れれば、アセンブラ抜きでアセンブルした関数を使用できるでしょう(やってないけど。)
なお、上記のアセンブラはArm Thumb(16bit幅のバイナリを使う)用のアセンブラです。ラズパイPico2の場合 Arm Cortex M33コアなので、Thumb2命令も使えます。その場合は拡張されたアセンブラがあるようなのでそちらをロードする必要があるようです。
defcodeスペシャル・フォームのABI
上記の解説ページに記されている defcodeスペシャル・フォームのABI的なものを勝手にまとめると以下のようです。
-
- 定義する関数には最大4パラメータを渡せる。パラメータは r0からr3で渡される(整数の場合は整数、整数以外はアドレス)
- 戻り値はr0で返す。整数値。
- r0-r3, r12, r14(LR)は破壊してもよい。ただし関数から戻るときには、リンクレジスタを使って($bx ‘lr)のようにして戻るので、その保存は自己責任。その他のレジスタを使用する場合はPUSH、POPで保存する。
- マシンコードを16進記述する場合は、#x210dのように普通に定数として記述。アセンブリ言語命令のニーモニックを記述する場合は、ニーモニック先頭に$プレフィックスがつく
- $word関数によりインライン32ビット定数を挿入できる。アライメントは自動で調整される。
- ユーティリティ関数 x16 で、16ビット値をHEX表示可能
- (追加)コードは相対アドレッシング使ってポジションインデペンデントにすること。
最小?の関数でテスト
さて以下のmyadd 関数、2つの引数をとってその和を返すもの、をuLispアセンブラを使って記述してみました。
(defcode myadd (x y) ($add 'r0 'r1) ($bx 'lr))
引き数2個(整数)を加えて uLisp処理系に加算結果を返すだけのもの。
アセンブラできるみたいだね。uLisp半端ねえ。