小さいけどパワフルなFORTH言語について

小さいけどパワフルなFORTH言語

1981年1月、「マイコン用最新言語 FORTH vs PASCAL」という講習会の講師を務めました。FORTH言語は、小さな処理系ですがとてもパワフルな言語です。
ワープロなどという便利なツールが無い時代ですから、講義のテキストはレポート用紙に手書きして作成しました。このブログは技術資料として、講習会テキスト(1981年執筆)からFORTHに関する部分を抜粋して加筆修正して掲載します。


講習会は、CSK社内でマイコンリレー講習会という企画で行いました。

その頃、私はコンピューターサービス株式会社(CSK)からM電器中央研究所に派遣され、U-300ミニコンでリアルタイムシステムをアセンブラ言語で開発していました。

M電器でリアルタイムシステム開発

マイコンについては、大学生のときにマイクロプロセッサ インテル8080を企業実習で初めて使いました。(「ベンチャー企業V社でインテル8080を使う」
その後、NECのTK-80というマイコン・ボードを購入して趣味でインテル8080のプログラミングをしていました。(「TK-80トレーニングキットで趣味のプログラミング」

しかし、FORTHもPASCALも実際の開発で使用したことはありませんでした。
FORTHについては、1Dayセミナーを受講した程度です。このセミナーで講師の方は、FORTHの仕組みやメリットについて熱く語っていたのが印象的でした。

コンピュータサイエンスbit誌や情報処理学会誌などを参考として自分の理解したことを整理して発表したわけです。

プロローグ

半導体技術の急速な進歩に伴って、4ビットマイクロプロセッサが開発されてからわずか10年の間に16/32ビットマイクロプロセッサが登場しています。また、マイクロプロセッサが高機能になり、アプリケーション・システムも複雑になってきています。
最近の技術革新によってハードウェアの価格が低下し、ソフトウェア開発の主要部分を占める人件費が増大しています。その結果、ソフトウェア生産性向上を目的として、現在主流であるアセンブラ言語から高級言語を使用する傾向が強くなってきています。
高級言語を使用すると、アセンブラ言語を使うときに比べて1/2あるいは1/4の行数で書ける上、プログラムが理解しやすくなりソフトウェア生産性が向上します。しかし、その反面アセンブラ言語で書くときに比べて高級言語で書かれたプログラムから生成される機械語は、使用メモリ量が増え、実行時間が増大する欠点を持っています。この比は、コンパイラのプログラム最適化レベルにより差がありますが、平均的に1.2~2倍の差があると言われています。この数値は、熟練プログラマーによるもので、初心者の場合は逆にアセンブラ言語で書いた方が効率が悪くなることもあります。
このような現状において、効率/生産性のトレードオフでソフトウェア開発は少しずつ高級言語へ移行するものと思います。

FORTH言語の概要

1969年、米国 NRAOにおいて電波望遠鏡によるデータ収録解析システムを開発していた、チャールズ・ムーアが考案したミニコン用システム開発言語です。

FORTHの名称は、Fourth Generation Computer からきており、次世代(第3世代の次)のコンピュータのコンセプトを先取りしようという意志と自信が込められています。

FORTHは、現場にいた一介のプログラマーによって作られ、現場でソフトウェア開発する人々の手で普及した構造化プログラミング向きの言語です。

What is FORTH

  • FORTHは、それ自体が完全な基本ソフト(OS)であり、対話式のコンパイラ、マクロアセンブラ、デバッガを持っている。
  • FORTHは、システム開発用の高水準言語である。
    • BASIC言語のように、会話形式でプログラミングできる。
    • PASCAL言語のように、構造化プログラミングの思想に依拠している。
    • C言語のように、細かな処理の記述が可能である。
    • LISP言語のように、自己増殖可能で機能を無制限に拡張できる。
  • FORTHは、アセンブラを内蔵している。
    • アセンブラルーチンとの外部結合は不要である。
    • 内蔵アセンブラにより定義したエレメントは、そのまま命令として使用可能である。
  • FORTHは、FORTH以外のソフトウェアを一切必要としない。

FORTHのアプローチ

アーキテクチャの異なるCPUを小さなソフトウェアで包んで『FORTH-Computer』という仮想計算機を再構成します。

FORTHのアプローチ

FORTHのアプローチ

FORTHシステムは、特定のプロセッサにFORTH言語と機械語で核(コア)となる『FORTH-Computer』を作ります。その上に、FORTH言語でアセンブラ、コンパイラ、エディタやFORTH基本ワードを実装します。
従って、新しいCPUに実装するには、単に核の部分の機械語をプログラムするだけなので、トランスポータビリティが高いです。

FORTHシステムは、CPUに依存しない言語オペレーティングシステムです。FORTH言語で書いたアプリケーションプログラムはどんなCPUでも走り、CPUを変えてもプログラムは新規に作り直す必要はありません。

FORTHの構造

FORTHの構造


FORTHの最大の特徴

  • スタック演算 : Parameter Stack
  • 命令の拡張 : Word と Dictionary

仮想計算機『FORTH-Computer』の概念モデルを以下に示します。

FORTHの概念モデル

FORTHの概念モデル

  • ① : 外部メモリの内容をアドレス指定することで、スタックにPUSH/POPできる。
  • ② : 一般的なCPUのレジスタ/アキュムレータの機能としてスタックのセルを使用する。
  • ③ : 命令(Word)は、Dictionaryに登録されているので、Dictionaryを参照することで命令をデコードする。
  • ④ : 既存の命令(Word)を組み合わせることで、新しい命令(Word)を定義してDictionaryに登録する。

命令拡張への方法論

  • Shell (殻)アプローチ・・・すべての機能を盛り込む
  • Core(核)アプローチ・・・必要最小限の機能で構成し、目的別に拡張可能
拡張のアプローチ

拡張のアプローチ

Word と Dictionary

FORTH言語によるプログラミングは、仮想マシン『FORTH-Computer』を媒介してWordの定義を繰り返していくことで、プログラムを作成していきます。Wordの定義は、Dictionaryに登録します。

従って完成したシステムは、理想的なモジュール構造を構築しています。

FRUIT is [果物の説明]
RED is [赤いの説明]
ROUND is [丸いの説明]
APPLE is ROUND RED FRUIT

STATIONERY is [文房具の説明]
WRITING is [筆記の説明]
PEN is STATIONERY WRITING

APPLE-PEN is APPLE PEN
・・・

SYSTEM is ○ △ □ ☆・・・

FORTH is MULTI-LEVEL LANGUAGE

Wordは、FORTHによるコロン定義とアセンブラによるCODE定義のいずれかで定義します。

  • 複数の言語プロセッサの出力オブジェクトを外部から連結するわけではありません。
  • ひとたびディクショナリに登録した後は、コロン定義かCODE定義かの区別は意識しません。
  • CODE定義の適切な使用で、ランタイム・オーバーヘッドを完全にコントロールできます。
MULTI-LEVEL FORTH

MULTI-LEVEL FORTH

コロン定義

ワード名 定義 動作
SUM3 : SUM3 + + ; 3つの数の和
AVE3 : AVE3 SUM3 3 / ; 3つの数の平均

CODE定義

FORTHのアセンブラは、FORTH風の記法になります。命令ニーモニックは、CPUアーキテクチャごとに異なりますが、文法・制御構造・疑似命令などは共通です。

インテル8080 CPUで、ポート(0x1E)からパルス出力する例を示します。

CODE OUT-START 1 A MVI 0x1E OUT NEXT JMP
CODE OUT-STOP 0 A MVI 0x1E OUT NEXT JMP

: PULSE OUT-START WAIT OUT-STOP ;

CODE定義が必要になるケースは、限られます。

  • 割り込み処理、ハードウェア処理のうち、アセンブラでなければ記述できない場合
  • コロン定義による微妙なオーバーヘッドを無視できないほど、リアルタイム性を要求する場合

パラメータスタック演算

スタック演算とは

演算式は、逆ポーランド記法で表します。すべての命令のオペランドは、スタックに存在して演算はパラメータースタックで行います。

たとえば、計算式「(B-C)*F/(H+J)」は、逆ポーランド記法にすると「 B C – F * H J + /」となります。

スタック演算

スタック演算

逆ポーランド記法を意識するのではなく、現在のスタック状態だけを意識すれば良いことになります。

数値演算基本ワードの例

ワード名 機能 スタック状態 説明
加算 n1 n2 → n n=n1+n2
減算 n1 n2 → n n=n1-n2
乗算 n1 n2 → n n=n1*n2
除算 n1 n2 → n n=n1/n2
MOD 剰余 n1 n2 → n n=n1%n2

比較演算基本ワードの例

比較結果は、真(true)を1、偽(false)を0とします。

ワード名 機能 スタック状態 説明
比較 小なり n1 n2 → b b=(n1<n2)?1:0
比較 大なり n1 n2 → b b=(n1>n2)?1:0
比較 等しい n1 n2 → b b=(n1==n2)?1:0

スタック操作基本ワードの例

ワード名 機能 スタック状態 説明
DROP TOP破棄 n → pop(W)
DUP TOP複製 n → n n pop(W) push(W) push(W)
SWAP 2nd⇔1st交換 a b → b a pop(B) pop(A) push(B) push(A)

メモリ操作

大域変数の定義

大域変数を定義するとき、VARIABLE ワードを使用します。

VARIABLE 変数名

VARIABLE ワードの次に変数名を指定すると、メモリに変数領域を割り当て、変数名を新しいワードとして辞書に登録します。変数名ワードは、変数領域のアドレスをスタックにPUSHします。

メモリ操作ワードの例

ワード名 機能 スタック状態 説明
@ ロード adr → val val=*(adr)
! ストア val adr → *(adr)=val
+! メモリ加算 val adr → *(adr)=*(adr)+val

変数DATに値10を格納するには、以下のようにします。

VARIABLE  DAT
10 DAT !

FORTHの内部処理

スタック

FORTHは、2種類のスタックがあります。

  • パラメーター・スタック : 演算、ワード間パラメーター引数として使用
  • リターン・スタック : ワードのリターンアドレス

スタックを使用することにより、以下のようなメリットがあります。

  1. メモリ使用量の減少
  2. モジュール間のパラメーター引き渡しが明解
  3. 各モジュールが完全にre-usable(再使用可能)になります

ディクショナリ

Wordの定義は、ヘッダー部、名前部、リンク部、そしてWord内容の定義から構成します。ディクショナリに登録したWordは、リンク部により接続しています。

【パルス出力 FORTHプログラム】
CODE OUT-START 1 A MVI 0x1E OUT NEXT JMP
CODE OUT-STOP 0 A MVI 0x1E OUT NEXT JMP

: PULSE OUT-START WAIT OUT-STOP ;

パルス出力するFORTHプログラムのWord定義は、以下のようになります。

FORTH Word定義

FORTH Word定義

FORTHの有用性

FORTHは、ワードからワードへのアドレスリンクを順次追ってアドレス・インタプリタで実行します。ソースコード(中間言語)のインタプリタでないので、実行時のオーバーヘッドがほとんどありません。

アドレス・インタプリタのオーバーヘッドをFORTH固有の問題として過大に論じることは、あまり意味がありません。

  1. 下位モジュールコールによるオーバーヘッドは、モジュール構造化したプログラムに不可避の問題です。
  2. ソフトウェア開発の方法論は、実行時効率(速度、メモリ使用量)と開発・保守効率とのトレードオフです。
  3. より重要な問題は、システム全体の中であるモジュールが高速実行する必要があれば、そのことを可能にする手段があるか否かでです。(FORTH is Multi-Level Language)

ターゲット・コンパイル

マイクロコンピュータ応用システムでは、ソフトウェア開発環境とターゲット・システムの実行環境が異なる場合がほとんどです。ターゲット・システムでROMに書き込む場合、Read Onlyのプログラム領域とRead/Writeするデータ領域が連続するアドレスに混在すると、困ったことになります。

ターゲット・コンパイラは、ターゲット用に圧縮したオブジェクト・プログラムを生成し、ROM領域/RAM領域の自由なマッピングが可能です。そして、FORTHシステムのさらに便利な点は、開発システムのFORTH機能から任意のモジュールをターゲット・システムに自由に変更して持ち込めます。それは、FORTHシステムはすべてFORTHで書かれており、ユーザーにソースコードを公開しているから可能なのです。

ターゲット・コンパイルとは、

  • ディクショナリから不要なワード(開発用など)を除いて、コア部(核)を再構築します。
  • ワード定義から、不要なフィールド(名前、リンクなど)を除去します。

エピローグ

マイコンの応用分野が広がるにつれて、プログラムの生産性・信頼性・保守性を高める上で大きな効果がある各種の高級言語を使用するようになります。そこで、我々ソフトウェア技術者は、どんな言語でもマスターできる基礎力を身につける必要があります。