トップ 差分 一覧 Farm ソース 検索 ヘルプ RSS ログイン

Forthを作ってみる / スレッディングの変更

私の実装したForth、moiforthは、内部インタプリタをスイッチスレッディングで実装している。

       switch (prog[prog_cnt]) {
       case CODE_PUSH:
           処理
           break;
       case CODE_PLUS:
           処理
           break;

これを、コールスレッディングに変える。コールスレッディングに変えると、ワード処理は個別の関数に分けられ、それを呼び出すのは次のような処理になる:

       while (1) {
           (*prog[prog_cnt])();
       }

なにがうれしいかと言うと、

  • 内部インタプリタがコンパクトになる
  • 処理が個々の関数に分けられ、すっきりする

ただ、うれしくない部分もあり、

  • 遅くなる。スイッチスレッディングは分岐だが、コールスレッディングはサブルーチンコールになるため

いろいろ悩んだが、コールスレッディングにした。なぜなら、まだ書いたことがない、から。

スレッディングについては、以下に詳しく書かれている。

上記のコールスレッディングは、直接スレッディングなので、ジャンプ先アドレスがそのまま入っているが、これから作るのは間接スレッディングなので、以下のようになる。

       while (1) {
           (*jump_table[prog[prog_cnt++]])();
       }

当然直接スレッディングより遅い。しかし、prog[]のコードを番地のサイズより小さくすれば、メモリ効率が良くなる。PCではそんなことは関係ないが、組み込み用途だと意味がある。かも。

実際のコードは、こうなった。

   while (prog[prog_cnt] != CODE_END) {
       (*jump_table[prog[prog_cnt++]])();
   }

ループを抜けるには、プログラムの終了判定をしなければならない。アセンブラならループの外にジャンプするだけで済むが、コールスレッディングだと、こうせざるを得ない。そのぶん遅くなる。

つぎに、ワードの処理を関数単位に分ける。doを例にすると、こうなる。

/** 
 * ワード'do'を実行する
 */
void call_do(void)
{
    loop_pos--;

    /* スタックの内容をループスタックに入れる */
    loop_cnt_stack[loop_pos] = pop();
    loop_end_stack[loop_pos] = pop();
}

さらに、ジャンプ先となる関数ポインタの配列を作った。

void (*jump_table[])(void) = {  /**< ワードのジャンプ先テーブル */
    NULL, call_push, plus, print, word_if, word_else, word_then, call_do,
    call_loop, call_i, call_cr, call_word, proc_ret
};

新しいスレッディングのmoiforthを、テストする。testfile以下のプログラムを次々テストしていくが、問題ない。というわけで、スイッチスレッディングからコールスレッディングの変更が終わった。

ソースコード

戻る 前へ 次へ

moiforth-0.8.zip