Forth83 と Threaded Code
Forththreaded code
2022-10-25 1:04 JST

Forth83 にのヘッダについて調べた。このヘッダの形式は今でも小さなシステムに使われている。

F83 Header

日本語の「標準 FORTH」(私が持っているのは1986の初版第3刷)に詳しいのだがForth83 は 31 文字までのワードしか扱えない。Fig-Forth と LFA の位置が違う。NFA の h80 は何の意味があるのだろう?gforth では alias-mask となっている。CFA は通常ワード境界(16bit なら 2byte バウンダリ)で、必要に応じてパディングが入る。

■ gforth での実装されている Forth83 の検索プログラム

短いソースなので、そう読むのに苦労はしない(はず)。動きは関数名が表すとおり。(find-samelen)で $1F を andしているところがマスクして(フラグを無視するために)長さを取得しているところ。cell+@でリンクフィールドを追っている。

kernel/prim.fs
: (f83find)  ( c_addr u f83name1 -- f83name2 )
    BEGIN  dup WHILE  (find-samelen)  dup  WHILE
    >r 2dup r@ cell+ char+ capscomp  0=
    IF  2drop r>  EXIT  THEN
    r> @
    REPEAT  THEN  nip nip ;

: (find-samelen) ( u f83name1 -- u f83name2/0 )
    BEGIN  2dup cell+ c@ $1F and <> WHILE  @  dup 0= UNTIL  THEN ;

: capscomp ( c_addr1 u c_addr2 -- n )
 swap bounds
 ?DO  dup c@ I c@ <>
     IF  dup c@ toupper I c@ toupper =
     ELSE  true  THEN  WHILE  1+  LOOP  drop 0
 ELSE  c@ toupper I c@ toupper - unloop  THEN  sgn ;

: sgn ( n -- -1/0/1 )
 dup 0= IF EXIT THEN  0< 2* 1+ ;

TIB

Forth83 も gforth もTIB(Text Input Buffer) を通して入力するシステムになっている。

Code: と Code

gforth の cross 環境ではプリミティブを定義するのに Code: と Code がある。プリミティブの内部はスレデッドではない。前者はインターナルに使われるワードを定義するものに使われている。なのでちゃんとしたヘッダーがない。Code は普通のプリミティブを定義するのに使われているので、ヘッダがある。通常の環境(cross じゃない環境)にも Code というワードはある。

Threaded Code

Threaded Code の理解が進んだ。まず名称。Forth ではIFELSETHENでさえ、別々のワードになっている。C 言語などでは if 〜 else 〜 then は連続したコードになるのでThreaded ではない。

ELSEは IF の後にくるだろうという前提の上で作られており、ELSE 単独で突然出てくることはない。が、つじつまが合えば次のような事もかけてしまう。2個目以降の ELSE はもはや何を意味しているかわからない。

kernel/prim.fs
: my-else-test0 IF ." abc " ELSE ." ghi " THEN ." xyz" ;
: my-else-test1 IF ." abc " ELSE ." def " ELSE ." ghi " THEN ." xyz" ;
: my-else-test2 IF ." abc " ELSE ." def " ELSE ." ghi " ELSE ." jkl" THEN ." xyz" ;

ITC(Indirect Threaded Code)

ITC と略されて最初何のことだかわからなかった。これはワードが Primitive と Threaded Code の2種類あることが一因。Primitive の場合は直接そこへジャンプしないといけない。Threaded Code のときは Threaded になっているコードへのアドレスの並びを順序よく実行する。そのための特別なプリミティブがある(docol)。Indirect にするには docol は 8086 の場合は次のようなコードになる。

arch/8086/prim.fs
  Code: :docol
     frp dec, frp dec, fip frp ) mov,         save ip
     2 w d) fip lea,                          calc pfa
     next,
  End-Code

また next, はアセンブラで次のようになる。

arch/8086/prim.fs
  ' di Alias w

  : next,
    lods,
    ax w xchg,
    w ) jmp, ;

このとき(Indirect なので) w (実は di レジスタ) にアドレスがセーブされている。これを docol はパラメタとして使う。docol を読むと巧妙に w が使われているのがわかる。

これで Threaded なアドレスの列を実行することが出来たとしよう。あとは通常の Primitive も同じ仕組みで実行できれば良い。そこで、Primitive で実行される実際のアドレスの前に1個のアドレスリストを挟めば形式として Threaded Code と同じになる。通常そのアドレスリストはヘッダ内に CFA として書かれている。

絵がないと何を書いているのかさっぱりわからない。こんどスライドで説明できるようにしよう。

リンク集