Gforth は、 アセンブリ言語でワードを実装する方法(abi-code
… end-code
を使用)と、
任意の実行時の振る舞いを持つ定義ワードを定義する方法(does>
のようなの)を提供し、 ここで、
この実行時の振る舞いを(does>
とは異なり、) Forth ではなくアセンブリ言語で定義します。
ただし、 Gforth のマシン非依存の性質により、 いくつかの問題が生じます。 まず、 Gforth は複数のアーキテクチャー上で実行されるため、
標準のアセンブラーを提供できません。 ただし、 実行されるいくつかのアーキテクチャー用のアセンブラーは提供されています。 さらに言えば、 Gforth
ではシステムに依存しないアセンブラーを使用したり、 ,
や c,
を使用してマシン・コードを直接コンパイルしたりできます。
もう 1 つの問題は、Gforth の仮想マシンのレジスター(スタック・ポインターと仮想マシン命令ポインター)がインストールとエンジンに依存することです。 また、 どのレジスタを自由に使用できるかは、 インストールとエンジンによって異なります。 したがって、 Gforth 仮想マシンのコンテキストで実行するように記述されたコードは、 基本的に、 そのコードが開発されたインストールとエンジンに限定されます(たまたま、 他の場所でも動く可能性はありますが、 それに頼ることはできません)。
幸いなことに、 同じ呼び出し規約(ABI)を持つプラットフォーム上で実行されている Gforth に移植可能(portable)な
abi-code
ワードを Gforth で定義できます。 通常、 これは同じアーキテクチャーと OS の組み合わせへの移植性を意味し、
しばしば OS の境を越えることができます。
assembler
( – ) tools-ext “assembler”
ボキャブラリー: 検索順序スタック(the search order)のTOSのワードリストを assembler ワードリストに置き換えます。
init-asm
( – ) gforth-0.2 “init-asm”
assembler ワードリストを検索順序スタック(the search order)にプッシュします(訳注:つまり assembler ワードリストが検索順序スタックのTOSになる)。
abi-code
( "name" – colon-sys ) gforth-1.0 “abi-code”
C言語プロトタイプ(C-prototype)に対応するプラットフォームの ABI 規則を使用して呼び出されるネイティブ・コード定義を開始します:
Cell *function(Cell *sp, Float **fpp);
ここで、 FP スタック・ポインターは、 FP スタック・ポインターを含むメモリー位置への参照を提供することによって渡され、 (必要な場合)変更された FP スタック・ポインターをそこに格納することによって渡されます。
;abi-code
( – ) gforth-1.0 “semicolon-abi-code”
コロン定義を終了しますが、 実行時に最後に定義されたワード X (create
で作られたワードである必要があります)の変更もして、 C言語プロトタイプに対応するプラットフォームの ABI
規約を使用してネイティブ・コードを呼び出します:
Cell *function(Cell *sp, Float **fpp, Address body);
FP スタック・ポインターは、 FP スタック・ポインターを含むメモリー位置への参照を提供することによって渡され、 変更された FP スタック・ポインターをそこに格納することによって渡されます(必要な場合)。 パラメーター body は X の本体です。
end-code
( colon-sys – ) gforth-0.2 “end-code”
コード定義を終了します。 ABI 呼び出しからの戻り(abi-code
の場合)、 または次の VM
命令へのディスパッチ(code
および ;code
の場合)を自分でアセンブルする必要があることに注意してください。
code
( "name" – colon-sys ) tools-ext “code”
Gforth 仮想マシン(エンジン)のコンテキストで実行されるネイティブ・コード定義を開始します。 このような定義は Gforth
インストール間で移植できないため、 code
の代わりに abi-code
を使用することをお勧めします。
code
定義は、 次の仮想マシン命令へのディスパッチで終了する必要があります。
;code
( compilation. colon-sys1 – colon-sys2 ) tools-ext “semicolon-code”
;code
の後のコードが、 最後に定義されたワード(create
されたワードである必要があります)
の振る舞いになります。 code
の場合と同じ注意事項が適用されるため、 代わりに ;abi-code
を使用することをお勧めします。
flush-icache
( c-addr u – ) gforth-0.2 “flush-icache”
プロセッサの命令キャッシュ(存在する場合)の c-addr と u バイト以降に古いデータが含まれていないことを確認してください。
END-CODE
は flush-icache
を自動的に実行します。 注意(Caveat):
flush-icache
はあなたのインストール環境では機能しない可能性があります。 これは通常、
マシンでダイレクト・スレッドがサポートされておらず(machine.h を確認してください)、
マシンに別個の命令キャッシュがある場合に当てはまります。 このような場合、 flush-icache
は命令キャッシュをフラッシュする代わりに何も行いません。
flush-icache
が正しく動作しない場合、 abi-code
ワードなども(まず確実に)動作しません。
これらのワードの一般的な使用法は、 同等の高レベルの定義ワードから類推することで最も簡単に示すことができます:
: foo abi-code foo <high-level Forth words> <assembler> ; end-code : bar : bar <high-level Forth words> <high-level Forth words> CREATE CREATE <high-level Forth words> <high-level Forth words> DOES> ;code <high-level Forth words> <assembler> ; end-code
abi-code
を使用する場合は、 あなたのプラットフォームの ABI ドキュメントを参照して、
パラメーターがどのように渡されるか(スタック・ポインターをどこで取得するかがわかります)、
戻り値がどのように渡されるか(データ・スタック・ポインターがどこに返されるかがわかります)を確認してください。 ABI のドキュメントでは、
どのレジスターが呼び出し元によって保存されるか(caller-saved)や、 コード内で自由に破棄できるかや、
どのレジスターが呼び出されたワードによって保存される必要があるか(callee-saved)についても説明されています。
あなたはそれらを使用する前に保存し、 後で復元します。 一部のアーキテクチャーと OS
については、適切なセクションで呼び出し規約の各部分の短い概要を示します。 リバース・エンジニアリング志向の人々は、 see
abi-call
を通じてスタック・ポインターの受け渡しと戻りについて知ることもできます。
ほとんどの ABI はレジスターを介してパラメーターを渡しますが、 一部の ABI(特に最も一般的な 386 (別名 IA-32) 呼び出し規約) はアーキテクチャー・スタック上でパラメータを渡します。 共通の ABI はすべてレジスターで戻り値を渡します。
abi-code
を使用する際に知っておく必要があるその他のことは、 Gforth ではデータと FP
スタックの両方が下方向(下位アドレスに向かって)に成長し、 セルあたりのサイズは 1 cells
になり、 FP 値ごとのサイズは
1 floats
になるということです。
386 アーキテクチャーで abi-code
を使用する例を以下に示します:
abi-code my+ ( n1 n2 -- n ) 4 sp d) ax mov \ sp into return reg ax ) cx mov \ tos 4 # ax add \ update sp (pop) cx ax ) add \ sec = sec+tos ret \ return from my+ end-code
この例の AMD64 バリエーションは AMD64 (x86_64) Assembler にあります。
386 で FP 値を扱う例を以下に示します:
abi-code my-f+ ( r1 r2 -- r ) 8 sp d) cx mov \ load address of fp cx ) dx mov \ load fp .fl dx ) fld \ r2 8 # dx add \ update fp .fl dx ) fadd \ r1+r2 .fl dx ) fstp \ store r dx cx ) mov \ store new fp 4 sp d) ax mov \ sp into return reg ret \ return from my-f+ end-code