はじめに
この記事は、「キリーの日本語プログラミング」ブログに掲載したコラムを編集したものです。Mindアドベントカレンダー2024に投稿します。
日本語プログラミング言語Mind前史 - スタックフレームとFORTHというQiitaの記事を詳細にした内容となります。
この記事で言う「FORTH」という言葉は真正なFORTH言語というよりは、もう少し広く「スタック指向のプログラム言語」や「スタック指向の命令体系」を指すのにあえてFORTHと書いています。FORTH言語をよくご存じの方がご覧になると、言語特有の自己増殖スタイルの話とか、インタプリティブな開発環境(即コンパイル・即実行)など、一番おいしい所が抜けているように思われるかも知れませんが、その点ご承知をお願いします。
スタックコンピュータとFORTH(2)
私のCPUについての知識は、その昔マイコン時代のエンジニアが皆そうだったようにCPUについて多少の知識があった(特にプログラムをアセンブラで書く場合)という程度のもので、この記事で間違ったことを書いていたらご指摘いただけるとありがたいです。
また、取り上げる書籍が古いのはもちろんですが私のハードウェア知識も古いままなのでそのあたりはご容赦ください。
---
前回と重複するが書籍「スタックコンピュータ」の情報から。
その書籍とは 1994年、「スタックコンピュータ」、Philip J. Koopman, Jr.著、田中清臣監訳/藤井敬雄訳、共立出版である。中古本がアマゾンで売られている。
本書ではスタックコンピュータの歴史から始めてスタックマシンの特徴の解説や、実際に使われたマシンの解説(ニーモニックのリストを含む)など、実に多義かつ詳細にまとめている。
古い話が多いが現在でも参考になることは多く、スタックマシンを系統立ててじっくり勉強するのに向いていると思う。
値段(5千円超)もさることながら、ボリュームもあり、正直に言うと私は購入時にざっと斜め読みしただけでしばらくそのままにしていた。その後2、3回ほど読み直し、今回この記事を書くために改めて読み直したが、それでも末尾の付録を除いた本文として6割ぐらいしか読めていない。
FORTHの綴りについて。
古くから FORTH と全部大文字で書いていたのだが、本書の書き方や、FORTH社のHPを見ても Forth と書いているようなのでこの2回目の記事ではそのようにした。全部大文字は(社名の)FORTH社を指すときぐらいなのかも知れない。
なお本書とは別だが、ごく手短にスタックコンピュータのことを知りたい場合はウィキペディアの「スタックマシン」の解説を参照されたい。
時代背景について。
訳書は1994年発行だが原書の時期が気になったのでWebで調べるとインターネット上にデジタルコンテンツが見つかり1989年と分かった。
Stack Computers: the new wave -- an on-line book
https://users.ece.cmu.edu/~koopman/stack_computers/index.html
(カーネギーメロン大学)
訳書で冒頭の「原著序文」にはこのようなことが書かれている。
/ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄\
この本は Novix の NC4016 の開発により口火が切られたスタックコンピュータの新しい流れに関して書かれている。読者のなかには勘違いして Burroughs や HP のスタックマシン類を想定するかもしれないが,この本の焦点はまったく異なる。こられの新しいスタックコンピュータはまったく異なった技術とアプリケーション分野のトレードオフを含み,旧世代のスタックコンピュータとはまったく似ていないマシンになっている。
\___________________________/
私はだいぶ昔、誰かから聞いた話として、スタックを明に使うコンピュータとしてはバローズのメインフレームあたりが最初でずっと連綿と続いていると思っていたのだが、上記を読むと世代が2つに分かれるようだ。
私はこの本によく登場する Novix NC4016 とか、Harris RTX 2000 などはまったく知らず、時代的にピンと来ないのだが、本書の中で(インテルの)80386、(モトローラの)68020 といった32bitプロセッサの話が登場するので、そのあたりの時期に書かれたのだと理解できた。ミニコンでは VAX という言葉も登場する。
またコンピュータ・アーキテクチャの議論として昔、CISC 対 RISC の話がよく登場したころがあったが、この用語も本書で随所に登場する。ちなみに著者は RISC にからめて次のようなことを言っている。
/ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄\
この意味において,スタックマシンは RISC の '縮小命令セットコンピュータ' に対して '縮小オペランドセットコンピュータ' であるといえる。
\___________________________/
上記にある「オペランド」は、本書でスタックコンピュータの特質をあらわすのに重要な意味を持っている。
さらに時代背景としては当然、プログラム言語 Forth との関係が気になるところだ。
Forthの歴史はだいぶ古く、チャールズ・ムーア氏が1968年に開発したとされている。本書(原書)が書かれるより12年前である。
( Forthの歴史については本家の FORTH, Inc. のサイトで以下が詳しい
Forth programming language, history and evolution
https://www.forth.com/resources/forth-programming-language/ )
また、本書冒頭で「Novix の NC4016 の開発により口火が切られた」と書かれたこのマシンはWebで調べると1985年らしく、NC4016 の開発はForth誕生から17年後ということになる。本書でも書かれているように、多くの(新世代の)スタックマシンは、Forthをより効率的に走らせることを主な目的としたようだ。
本書の趣旨からしてスタックコンピュータの有意性は随所に書かれているのだが、いくつか例を挙げる。
/ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄\
スタックマシンが最初に成功したアプリケーション分野は、リアルタイムの組み込み制御環境である。スタックマシンが、他のシステム設計アプローチよりずっと性能が優れている。
~略~
スタックマシンをプログラムすることは、従来のマシンよりもたやすく、スタックマシンのプログラムは他のプログラムより確実に走るとされている。
スタックマシンは、コンパイラがより簡単に書ける。なぜなら、コンパイラを複雑にする例外的ケースがより少なくなるからである。
~略~
(他の文献について触れたあと)
この本を見ていくにつれ、スタックマシンはまた、レジスタマシンよりある種のプログラム、特によくモジュール化されたプログラムを実行されることでもずっと優れていることがわかる。また、他のマシンよりシンプルであり、小さなハードウェアで大変優れた処理能力をだす。スタックマシンの特に有望なアプリケーションは、リアルタイムの組み込み制御アプリケーションである。これには、小さなサイズ、高い処理能力、そして割り込みハンドリングの優れたサポートの組み合わせが必要とされ、スタックマシンのみがこれらすべてを待たすことができるのである。
~略~
注目すべき興味深い点は、これらのマシンのいくつかが、まずForthを実行するよう設計されたにもかかわらず、一般言語をはしらせるにも適していることである。
\___________________________/
上記で言われているような有意性にもかかわらず、残念ながら今現在、スタックマシン(CPUのレベルからスタック指向という意味で)が組み込み制御環境で広く使われるようにはなっていない(私が知らないだけかもしれないが)。
昔、マイクロプロセッサの 32bit化が始まったとき、性能的に優れていた Z8000 や 68000 ではなく、どちらかというと泥くさいアーキテクチャの8086が結果的に普及したことを思い起こさせる。
次のものはスタックコンピュータという呼び方にはなじまないかも知れないが、プロセッサのレベルからのスタック指向で、かつ、組み込み用途と考えられなくもない。
・80x87(浮動小数演算コプロセッサ)
さらに少しややこしいが、「スタックマシンではない組み込み環境でのスタック指向プログラムの使われ方」(つまりCPUはスタック指向ではないがプログラムはスタック指向)というケースでは下記のように意外と多く使われている。
・プリンタ制御言語の PostScript
・通信用スクリプト言語の TeleScript の下位レイヤ
・Java の下位レイヤ(Java Virtual Machine)
(記:AndroidのJava VMはスタックマシンではないらしい)
前の引用の中で触れられている組み込み制御分野での有意性の根拠の一つ「割り込みハンドリングの優れたサポート」の具体的なものは以下のようだ。
割り込み発生と同時に、一般的なCPUではレジスタやフラグをメモリに退避する必要かあるが、スタックマシンではレジスタもフラグも無いのでその分高速に処理できる。
スタックマシンでは、演算中のデータはスタックにあるため、‥割り込み処理が主処理のスタックの続きを使うのであれば‥データの退避の必要が無いということである。
ただし、マルチタスク(マルチスレッド)あるいは、割り込みが完全な入れ子になっていない場合、「スタックの続き」を使うことができないため、スレッド毎に割り当てたスタック領域を切り替える必要がある。
本書では「6.3.5 コンテキスト切り替え」という独立項を設け、実験をふまえてそのコストについて論じている。そこで次のくだりは少し意外であった。
/ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄\
コンテキスト切り替えのオーバーヘッドはスタックマシンがなぜマルチタスキングに向かないのかという理由としてよく引き合いに出される。膨大なスタックバッファ空間をプログラムメモリに保存しなければならないというのがこの論拠になっている。しかしマルチタスキング処理においてスタックマシンが他のマシンより幾分劣っているというこの意見は明らかに誤りである。
~略~
このような低いオーバーヘッドはどのように達成されるのだろうか。理由の一つにはこれら三つの重い再帰プログラムを実行している間の平均的なスタックの深さはたったの12.1要素であることだ。ころさはスタック上にあまり情報が存在しないので、コンテキスト切り替え時に保存しなければならない情報がほとんどないことを意味する。
~略~
\___________________________/
私の推測だが、新世代のスタックマシンではスタック領域をオンチップの高速な場所に置いているため、タスク切り替え時に「退避」する必要がある‥ということのようだ。
私自身はいままでスタックマシンを触ったことがなく、あくまでプログラム言語としてのForthを通してスタックマシンをいわばエミュレートした環境で動かしていたにすぎなかった。
そのため、オンチップの高速なスタック領域については知らず、上記冒頭の書き出しを意外に思った(しかし著者はそれについても上記のように反論しているが)。
考えてみれば、レジスタに匹敵する高速なアクセスをスタックに持たせるのだろうからスタックはオンチップになり、そのサイズはメインメモリに比べればあまり大きくないということは理解できた。
脇道に逸れるが、スタックマシンは、というより、スタック指向のプログラムが、スタックを全面的に使うわりにはスタックはそれほど深くはならない‥ということは私も経験上分っていたので、前記実験でのスタック深さの平均が12要素という説は納得した。
本書の目玉「ハードウェアスタックの分類」
本書の目玉は、「第2章 ハードウェアスタックの分類」である。スタックコンピュータを三つの座標軸で分類するというものだが、実は多くのコンピュータ(CPU)をこれで分類できる。分類のための評価軸は以下となるという。
スタックの数 = 単一または複数
スタックバッファのサイズ = 小または大
オペランドの数 = 0,1または2
「スタックバッファのサイズ」については説明を要する。ここで言うスタックバッファとはスタック専用のメモリのことを指す。
「小さなスタックバッファ」
「小さなスタックバッファ」は、たとえばスタックTOPと2NDをすばやく(レジスタのように)アクセスできるよう工夫しておき、それより奥のスタック領域は普通のプログラムメモリ空間を使うもの‥などを指すようだが、そのような小さなバッファさえ持たないマシンでも分類上は「小」扱いとする。
「大きなスタックバッファ」
「大きなスタックバッファ」とは、スタック領域全体をオンチップに収容するものである。
私が思ったのは、筆者の命名とはうらはらに、「小さなスタックバッファ」のほうが、普通のメモリ空間にスタックを確保することからサイズの制限が無い、つまり大きいな領域が取れるという意味で、「小さい」/「大きい」が逆にも思えて妙な感じがした。
どちらも長所・短所があり、どちらが優れているというものでもなさそうだ。
汎用CPUをピックアップ
次は本書の分類表の中で私が知っているわずかのマシンについて(いずれもスタックマシンではなく汎用CPUだが)ピックアップしたものである。
SS2 Intel 80x86
MS1 PDP-11
MS2 Motorola 680x0
上記で左端の文字はスタックの数(Single/Multiple)を示し、2番目の文字がスタックバッファのサイズ(Small/Large)を、3番目の文字がオペランド数を示す。
たとえば 80x86 は Singleスタック、Smallサイズ、2オペランドのマシンとなる。
680x0 は Multipleスタックとして分類されている。80x86との違いはスタックの数である。68000ではどのレジスタにもスタックポインタの役割を持たせられるので Multiple となったのだろう
スタックコンピュータの本質
本書ではスタックコンピュータの本質をこの分類上、「複数スタック、0オペランドのマシン」と位置付ている。この「0オペランド」という言い方は私の興味を引いた。
オペランド数からCPUを分類し、仮にアセンブラ表記で書いてみると次のようになる。
2オペランドマシン
ADD R1, R2
1オペランドマシン
ADD R1
0オペランドマシン
ADD ←オペランドが無い!
上記で「1オペランドマシン」は、加算値は書かれるが、被加算値が省略される。この場合アキュムレータと呼ばれる暗黙のレジスタが演算対象となる。
「0オペランドマシン」は、加算値と被加算値が共に省略される。暗黙のデータはレジスタではなくスタックとなる。
確かにスタックマシンでは演算対象は スタックTOP とか TOPと2ND などに決まっているため、オペランドの指定は不要であり、したがって「0オペランド」と言うことはできる。当たり前のことなのだが、一般のマシンと同列に比較したことが私には面白かった。
それで、0オペランドだと何が良いのかが重要らしい。(Forthを効率的に動かせるという話は置いて)。
まずオペコードの数が極端に減る。オペコードの数は乱暴に言えば「命令の種類」×「オペランドの組み合わせ数」となるのだが、これが「命令数」だけになるのだから減るのは当然である。本書の中には次のくだりがある。
/ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄\
もう一つの利点は、256の異なったオペコードのために8ビット幅の命令フォーマットで足りるというように、個々の命令がきょくたんにコンパクトになることである。
\___________________________/
これよにり、オペコードが単純化され、それをデコードする(オペランドが指すアドレシングも含めて)ための仕組みも単純となる。
トランジスタ数も減って平均故障率も減り高速化されるという仕組みである。昔、CISC対RISCの議論があったが、RISCよりもっとRISCらしい構造と言える。
もちろんいまどきのCPUでは、たとえば8ビットというごく短い命令フォーマットが嬉しいというシーンはあまり無さそうだが(制御環境ではあるかも知れない)、私がこれで思い出したのがインテルの 8087 だった。(なぜか本書は 8087 には触れていない)
8087(もしくは 80x87)
8087(もしくは 80x87)は 8086 と協調して浮動小数演算を行うチップ(コプロセッサ)である。一時データはレジスタではなくスタックに保持するものでスタックマシン風の形態をとっている。
8087は8086のバスを流れる命令を常にウォッチしており、ESC命令を検出するとメインCPUである8086はバスを解放し、代わりに8087が動作を始める‥という、まさに両者は協調した動作をおこなう。
8087向けのオペコードは8086向けオペコード空間のわずかな空き領域(ESC命令がそれ)を使っており、当然ながら8087が使えるオペコードはかなり少ない。少ないオペコードでなんとかやりくりする必要がある。
結果として8087がスタックマシン風になっているのはインテルの設計者の好みではなく、必然だったと言えるだろう(私の勝手な推測として)。
ただ、8087 の命令体系を見るときれいなスタックマシンではない。たとえば「0オペランド」命令は少なく、1つ/2つのオペランドを持つことがほとんどだし、比較演算の結果はフラグに入る(本来のスタックマシンではフラグは使わない)など、相当変則的である。
また演算に特化しているため、分岐命令などプログラムとして必須の命令も無い。本書が 8087 を扱わなかったのはそのあたりが理由かも知れない。
8086/8087 が使われていた時代、私がやっていたリギーコーポレーションではForth系で8086向けの Fifth86 というコンパイラの浮動小数ライブラリでは浮動小数のForthワードをほぼ1対1で8087の命令にダイレクトに割り当てていた。たとえば、
F+
というForthワードは 8087 の
FADD
にそのまま対応させた。通常のデータスタックと別に8087のスタックを併用するために(浮動小数演算については8087スタックをそのまま使う)、スタック間移動ワードを設ける必要はあったが、浮動小数演算については大変能率の良いコードを生成できた。
8087が「スタックマシンもどき」でしかないにもかかわらずだいぶ書いてしまったが、8087が私がいままで唯一、身近に接することのできたスタックマシン(?)だからである。本書で紹介されているスタックマシンはまったく知らないものばかりなので‥。
「16ビットシステムのアーキテクチャ」
本書では「第4章 16ビットシステムのアーキテクチャ」から、いくつかの実際のスタックマシンについて解説をおこなっている。この章のはじめで筆者は次のように言っている。
/ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄\
ここで述べるシステムは16ビット幅にする第一の動機は、Forthプログラミングモデルが伝統的に16ビットであったことである。これは32Kバイト以下というForthプログラムの平均サイズと、64Kバイトのアドレス領域をもつマイクロプロセッサ上に初期のForthコンパイラが構築されたこととも符号する。
\___________________________/
最初で解説しているマシンは WISCというメーカーの CPU/16 である。IBM PC互換機のスロットに差し込むボードとしてリリースされたようだ。ディスクリート部品で作れらている。
/ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄\
このプロセッサはコンピュータ設計コース用の指導教材として役立っている。
~略~
CPU/16 は、従来の(人によっては、時代遅れの、というかもしれないが)74LS00 シリーズチップと、スタックとプログラムメモリ用に比較的低速な150nsスタティックRAMを用いて構成される。
\___________________________/
と書かれている。74LS00シリーズのくだりは少し驚いた(ALUとして74LS181)。私は長いことハードの仕事から遠ざかっているので、この 74LSxx 時代が知っている最後で少し親近感が沸いた。
一つ飛ばした次に解説されているのが以下である。
/ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄\
Novix 社の NC4016 は、かつては NC4000 と呼ばれており、Forthプログラム言語の基本命令を実行するために設計された、16ビットのスタックベースのマイクロプロセッサである。これは最初に作成されたシングルチップのForthコンピュータであり、以降続く設計に見られる機能の多くが初めて使用された。対象となるアプリケーションは、リアルタイム制御と、汎用プログラミングのためのForth言語の高速実行である。
\___________________________/
4NC4016 は、4000ゲート以下、121ピンのPGAにパッケージされているとのこと。
タイマ/カウンタもオンチップで用意されているようだ。
たぶんこれはインテルの8086のように、スタックマシンのその後の基礎となるようなプロセッサなのだろう。
次に解説されている RTX 2000 は、前記 NC4016 の派生らしい。
/ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄\
Harris Semiconductor 社の RTX 2000 は、Novix NC4016 から派生した、16ビットスタックプロセッサである。RTX 2000 はたいへん高度に集積されており、核となるプロセッサだけでなく、スタックメモリ、ハードウェア乗算器、カウンタ/タイマを単一チップ上に含んでいる。
~略~
RTX 2000 はハイエンドな16ビットマイクロコントローラ市場がターゲットである。RTX 2000 はセミカスタム技術で構成されるので、プロセッサの特殊バージョンを特定の設定アプリケーションのために作成することができる。
\___________________________/
RTX 2000 は、84ピンのPGAにパッケージされているとのこと。
(このあと第5章で32bitのスタックマシンについても言及があるが省略する)
さて上記のようなスタックマシン群で、ニーモニックに注目したい。
当然のことだが、Forthを走らせることを意識したスタックコンピュータのニーモニック(アセンブリ言語?)はForth言語とだいたい重なる。さらに本書で紹介されている新世代のスタックマシンのニーモニックはForthのワード名そのものである。(多くのスタックマシンではさらに拡張した命令を装備しているようだ)
たとえば整数値を二倍にする処理を * を使わずに書くとして、アセンブリ言語として
DUP
+
と書いたものと、Forth言語として
DUP +
と書いたものは効果として同じである。縦に書けばアセンブリ言語で横に書けば高水準言語となるわけで、大変わったマシンということになる。
ただ、上記は私があえて言ってみただけで、本書で紹介されているスタックマシンについて、そもそもアセンブリ言語なるものは存在しないかも知れない(意味が無い?)。残念ながら私はこれらのマシンに触ったことがないので本当のことは知らない。
最後に、本家のFORTH社(FORTH, Inc.)のホームページについて。
トップ画面に電波望遠鏡らしき写真があり同社が制御分野を主力にしていることが分る。同社のForthは以前は PolyFORTH だったが今は SwiftForth になったようだ。(アップル社の Swift とは関係がない旨コメントがある)
おわりに
最近、Mindユーザーの@mylifewithviolinさんがMind開発当時のCPUに興味を持たれているようなので、参考情報として投稿してみました。