Gforth を使った組み込み用の Forth の cross 開発についてちょっと調べた。
@ の実装
Swapforth の j1a の@ の実装は nuc.fs 内で次のようになっている。2000 番地と or したアドレスにジャンプすることで、その値が取れるようになっている。
j1a の実装側は抜粋すると分かりづらくなってしまうが次のようになっている。pc は 1bit ずれているので、mem_addr[13] に 1 が立っているかどうかをチェックしているのと等価。
ついでにメモリ側も調整。mem_addr_w[13] に 1 が立っていて メモリへの write はありえないので、無視するように設計されている。
この3つが微妙に絡み合っているのに加え、Python のプログラム(shell と読んでいる。UI のフロントエンド) の動き Tethered Mode の考慮があって、デバッグに手間取った。
蛇足ながら説明すると、Forth において @ はメモリ参照、! はメモリへの書き込み。
cross.fs 冒頭
Gforth で書かれている cross.fs の冒頭。variable で lst を宣言したその後、h# を定義している。Forth のマクロとも言うべき immediate とpostpone を使っている。immediate を使うと、入力された文字列を適宜変換できる。
マクロ(あるいはそれに準ずる機能)のあるインタプリタは便利だね。マクロというのは、ある特別なルールにしたがって入力された文字列を"即座に"(immediate に)変換して、変換したものに対して"後々"(postpone)実行をかけるものだ。
h# の使い方はh# 2000みたいな感じで 0x2000 をスタックに積むことができる。2000 を解釈するのは h# 内の記述(つまりマクロ的な記述=特別なルール)で 16 進数だと思いparse する。マクロ的な解釈が終わると postpone で literal としてスタックに積み上げる。
Forth が独特なのは入力を自分で一生懸命丁寧に位置文字ずつ解釈することかな。多くのインタプリタは自分自身で構文解析をしないよね。C で書かれた構文解析があって、その上でインタプリタとしてその言語が走る。Lisp の実装で、自分でミニLispもっていてそれが更に大きな Lisp を呼ぶというのもあるので、それと似たようなことをやっている言語はいくつもあるだろうけど、Forth はそこが徹底している。
Gforth と throw
Gforth は ANS Forth には(たぶん)ない throw というワードをサポートしている。とおもったあ Forth 2012 に throw exception の記述があった。
実際に h# を登録して動きを確認してみる。
h# 8000とやっても何も積まれない。Stack underflow だ。これはコンパイル時に有効になるマクロだから。ワードの定義時に使うように設計されているということなので、ワードを定義してみる。
:: で辞書を作成
なにをやっているか分かりづらいけど、::を定義しているコードを示す。これで ::で始まるワードの定義が予め設定されたメモリに領域に展開される。
例えばliteralの定義は次のようになっている。この辺は 16bit であるということが考慮されている。
h#は次の通り
basewords.fs と nuc.fs
cross.fs でクロスコンパイル環境を作り、一部、すでに word を登録した。この次に basewords.fs で基本となる j1a のインストラクションをワードとして定義していく。
その後、nuc で核(nucleus)となる Forth のワードを定義していく。cross.fs でクロスコンパイル環境を作り、一部、すでに word を登録した。この次に basewords.fs で基本となる j1a のインストラクションをワードとして定義していく。
その後、nuc.fs ではheaderというワードでj1a用のワードを定義していく。なんでだろう?headerheader の方がちょっと便利になっているかな?
最後に:mainが定義されていて、ここで Forth の REPL に入る。うまく leds とかを nuc.fs で定義できなかったので(どたばたしていたので勘違いかもしれない)header がどうやっているのかを正確に調べないといけない。今は postpone。
あと、:mainに飛ぶ方法がわからない。途中でワードを増やしたりすると:mainのアドレスが変わるけど、自動で計算して 0 番地にそこに飛ぶコードを差し込んでいる(はず)。
Forth に慣れていないとなかなか完璧に読みこなすのは難しい。
おおまかな流れはわかったので、あとは自分で超 mini Forth + VM とか作ってみればいいんだね。過去に Gforth で mini VM は作ったことがある。もう忘れちゃったけど、Gforth は VM を作るテンプレートを持っているんだよね。
まずはこんなところ。あとは FPGA にどう活かしていくかだね。