この章では、OpenBoot に実装されている Forth の概要を説明します。Forth プログラミング言語に詳しい読者も、この章の例を確認してください。これらの例には、OpenBoot に関連する特有の情報が含まれています。
OpenBoot に含まれる Forth のバージョンは、ANS Forth に準拠しています。付録 E 「Forth ワードリファレンス 」に全コマンドのリストが載せてあります。SBus デバイス用 OpenBoot FCode プログラムを書くための専用のワードについては、『Writing FCode 2.x Programs』マニュアルに説明されています。
この章では、読者はユーザーインタフェースの起動、終了手順を知っているものとしています。ok プロンプトで入力したコマンドのためにシステムがハングアップし、キー入力操作で回復できない場合は、正常動作に復帰させるために電源再投入を行う必要があるかも知れません。
Forth のコマンド構造は非常に単純です。Forth のコマンドは、Forth ワードとも呼ばれますが、印刷可能な文字、たとえば、英字、数字、句読記号 - の任意の組み合わせです。正しいワードの例を次に示します。
@ dump . 0< + probe-scsi |
probe-scsi コマンドとして認識されるためには、Forth ワードはそれぞれの間を 1 つまたはそれ以上の空白文字 (ブランク) で分離する必要があります。どのコマンド行の終わりで Return キーを押しても、そこまで入力したコマンドが実行されます。(この章に示すすべての例で、行の終わりでは Return が押されるものとしています。)
1 コマンド行に複数のワードを入力できます。1 行上の複数のワードは、左から右に向かって、つまり入力順に 1 つ 1 つ実行されます。たとえば、次の例
ok testa testb testc ok |
は次の 3 行と同じです。
ok testa ok testb ok testc ok |
OpenBoot では、大文字と小文字の区別はありません。したがって、testa、TESTA、TesTaはすべて同じコマンドを起動します。しかし、習慣によりコマンドは小文字で書きます。
コマンドによっては (たとえば、dump または words) 、大量の出力を生成するものがあります。そのようなコマンドは、q以外の任意のキーを押して中断できます。(q を押した場合は、出力は一時停止でなく強制終了されてしまいます。) コマンドを中断すると、出力は一時的に停止され、次のメッセージが表示されます。
More [<space>,<cr>,q] ? |
これに対して、スペースバー (<space>) を押して出力を再開するか、Return (<cr>) キーを押して 1 行出力し、再び休止するか、または q を入力してコマンドを強制終了します。出力を複数ページ生成する場合は、システムは自動的に各ページの終わりに上に示したプロンプトを表示します。
数値は、たとえば 55 とか -123 など、その値をキーボードで入力します。Forth は整数しか受け入れません。分数値 (たとえば、2/3) は入力できません。数値の終わりにピリオドを入力すると、それが倍精度であることを意味します。数値のなかにピリオド、コンマを埋め込んでも無視されます。したがって、5.77 は 577 と解釈されます。表記の規約では、区切り記号は通常 4 桁おきに使用します。数値は、1 つまたはそれ以上の空白文字を使用してワードや別の数値と区切ってください。
OpenBoot は 32 ビット整数の数値演算を行います。別に指定がないかぎり、数値はすべて 32 ビットです。
OpenBoot では 16 進の (変換) 基数を使用するよう奨励されていますが、必ずそうしなければならないわけではありません。したがって、正しく演算されるためにはコードが特定の基数に依存する場合は、そのような基数を設定する必要があります。
演算する数値の基数は octal、decimal 、hex といったコマンドを使用して変更できます。これらのコマンドは、以降の数値の入出力をそれぞれ 8、10、16 を基数として行わせます。
たとえば、10 進で演算するには、次のように入力します。
ok decimal ok |
16 進に変更するには、次のように入力します。
ok hex ok |
現在使用されている基数を知る 2 つの単純な方法を次に示します。
ok 10 .d 16 ok 10 1- . f ok |
上記の画面に表示されている 16 と f は、16 進で演算が行われることを示しています。10 と 9 が表示される場合は、10 を基数としていることを意味します。8 と 7 であれば、8 進を示します。
Forth のスタックは、数値情報の一時的保持用の後入れ先出し型 (LIFO) バッファーです。これを積み重ねられた本と考えてみてください。その場合、最後に置いた、つまり本の積み重ねの一番上に乗せた本から先に取ることになります。Forth を使用するには、スタックを理解することが不可欠です。
スタックに数値を入れる (一番上に乗せる) には、単にその値を入力します。
ok 44 (値 44 がスタックの一番上に乗る) ok 7 (値 7 がスタックの一番上に乗る) ok |
スタックの内容は通常は表示されません。しかし、希望する結果を得るためには、現在のスタックの内容を確認する必要があります。ok プロンプトが現れるごとにスタックの内容を表示することができますが、それには、次のように入力します。
ok showstack 44 7 ok 8 47 7 8 ok showstack ok |
一番上のスタック項目は、ok プロンプトのすぐ前に、リストの最後の項目としてつねに表示されます。上記の例では、一番上のスタック項目は 8 です。
前に showstack を実行している場合は、もう一度 noshowstack と入力すると、各プロンプトの前のスタック表示が削除されます。
この章のいくつかの例では showstack を有効にしています。それらの例では、各 ok プロンプトのすぐ前にそのときのスタックの内容が表示されています。それらの例は、スタックの内容が表示される点を除けば、showstack を有効にしてない場合と同じです。
数値変数を必要とするほとんどすべてのワードは、それらの変数をスタックの一番上から取り出します。また、返されるどの値も、通常、スタックの一番上に残され、別のコマンドで表示したり、「消費」する (つまり演算などに使ってスタックから削除する) ことができます。たとえば、+ という Forth ワードは、スタックから 2 つの数値を削除し、それらを加算し、結果をスタックに残します。次の例では、演算はすべて 16 進で行われます。
44 7 8 ok + 44 f ok + 53 ok |
2 つの値が加算されると、結果がスタックの一番上に乗せられます。Forth ワードの . は一番上のスタック項目を削除し、その値を画面に表示します。次の例を参照してください。
53 ok 12 53 12 ok . 12 53 ok . 53 ok (ここではスタックは空) ok 3 5 + . 8 ok (ここではスタックは空) ok . Stack Underflow ok |
規則に従うコーディング形式では、わかりやすいように Forth ワードの定義ごとに、それぞれの最初の定義行に (--) の形式のスタックダイアグラムを表記する必要があります。スタックダイアブラムは、ワードを実行するとスタックがどうなるかを指定するものです。
-- の左側に置かれる項目は、 スタックから消費 (つまり削除) され、そのワードの演算に使用されるスタック項目を示します。-- の右側に置かれる項目は、ワードの実行の終了後にスタックに残されるスタック項目を示します。たとえば、ワード + のスタックダイアグラムは (nu1 nu2 -- sum)であり、ワード. のスタックダイアグラムは (nu --)です。したがって、+ は 2 つの数値 (nu1 と nu2)を削除し、次にそれらの和 (sum) をスタックに残します。ワード . はスタックの一番上の数値 (nu) を削除し、それを表示します。
スタックの内容に影響しないワード (showstack や decimal など)のスタックダイアグラムは (--) になります。
場合によっては、ワードのすぐ後に別のワード、またはほかのテキストが必要なことがあります。たとえば、seeは、 see thisword (--)という形式で使用されます。
スタック項目は、正しい使い方がわかりやすいように、一般的に (意味を表すような) 説明的名前を使用して書きます。このマニュアルで使用するスタック項目の省略表記については、表 4-1を参照してください。
表 4-1 スタック項目の表記法
表記 |
説明 |
---|---|
| |
前後に空白文字を入れて表示される代替スタック結果。たとえば、( input -- addr len false | result true )。 |
| |
前後に空白文字なしで表示される代替スタック項目。たとえば、( input -- addr len | 0 result )。 |
??? |
未知のスタック項目 (1 つまたは複数)。 |
... |
未知のスタック項目 (1 つまたは複数)。スタックコメントの両側に使用した場合は、両側に同じスタック項目があることを意味します。 |
< > <space> |
空白区切り文字。先行空白文字は無視されます。 |
a-addr |
可変境界アドレス。 |
addr |
メモリーアドレス (一般的に仮想アドレス)。 |
addr len |
メモリー領域のアドレスと長さ。 |
byte bxxx |
8 ビット値 (32 ビットワードの下位バイト)。 |
char |
7 ビット値 (下位バイト)。上位ビットは不定。 |
cnt len size |
カウント値または長さ。 |
dxxx |
倍 (拡張) 精度数。2 スタック項目 - スタックの一番上の上位 quadlet (32 ビット)。 |
<eol> |
行末区切り文字。 |
false |
0 (false フラグ)。 |
ihandle |
パッケージのインスタンスのポインタ。 |
n n1 n2 n3 |
通常の符号付き値 (32 ビット) |
nu nu1 |
符号付きまたは符号なしの値 (32 ビット) |
<nothing> |
ゼロスタック項目。 |
phandle |
パッケージのポインタ。 |
phys |
物理アドレス (実際のハードウェアアドレス)。 |
phys.lo phys.hi |
物理アドレスの下位/上位セル。 |
pstr |
パックされた文字列。 |
quad qxxx |
quadlet (32 ビット値)。 |
qaddr |
quadlet (32 ビット値) 境界のアドレス。 |
{text} |
省略可能なテキスト。省略した場合は、デフォルト操作が行われます。 |
"text<delim>" |
入力バッファーテキスト。コマンドの実行時に構文解析されます。テキスト区切り文字を <> で囲みます。 |
[text<delim>] |
同じ行上のコマンドのすぐ後のテキスト。ただちに構文解析されます。テキスト区切り文字は <> で囲みます。 |
true |
-1 (true フラグ)。(真) |
uxxx |
符号なしの値、正の値 (32 ビット)。 |
virt |
仮想アドレス (ソフトウェアが使用するアドレス)。 |
waddr |
doublet (16 ビット) 境界のアドレス。 |
word wxxx |
doublet (16 ビット値、 - 32 ビットワードの下位 2 バイト)。 |
x x1 |
任意のスタック項目。 |
x.lo x.hi |
データ項目の最下位/最上位ビット |
xt |
実行トークン。 |
xxx? |
フラグ。名前は用途を示します (たとえば、 done? ok? error?)。 |
xyz-str xyz-len |
パックされない文字列のアドレスと長さ。 |
xyz-sys |
制御フロー用スタック項目。実装に依存します。 |
( C: -- ) |
コンパイルスタックダイアグラム。 |
( -- ) ( E: -- ) |
実行スタックダイアグラム。 |
( R: -- ) |
復帰スタックダイアグラム。 |
スタック操作用のコマンド (表 4-2で説明) では、スタック上の項目の追加、削除、並べ替えができます。
表 4-2 スタック操作コマンド
コマンド |
スタックダイアグラム |
説明 |
---|---|---|
( x1 x2 x3 -- x3 x1 x2 ) |
3 つのスタック項目を逆方向に回転します。 |
|
>r |
( x -- ) (R: -- x ) |
スタック項目を復帰スタックに転送します。 (使用には注意が必要です。) |
?dup |
( x -- x x | 0 ) |
一番上のスタック項目がゼロ以外の場合、複製します |
2drop |
( x1 x2 -- ) |
スタックから 2 つの項目を削除します。 |
2dup |
( x1 x2 -- x1 x2 x1 x2 ) |
2 つのスタック項目を複製します。 |
2over |
( x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2 ) |
初めから 2 つのスタック項目をコピーします。 |
2rot |
( x1 x2 x3 x4 x5 x6 -- x3 x4 x5 x6 x1 x2 ) |
3 対のスタック項目を回転します。 |
( x1 x2 x3 x4 -- x3 x4 x1 x2 ) |
2 対のスタック項目を入れ換えます。 |
|
3drop |
( x1 x2 x3 -- ) |
スタックから 3 つの項目を削除します。 |
3dup |
( x1 x2 x3 -- x1 x2 x3 x1 x2 x3 ) |
3 つのスタック項目を複製します。 |
( ??? -- ) |
スタックを空にします。 |
|
depth |
( -- u ) |
スタック上の項目数を返します。 |
( x -- ) |
一番上のスタック項目を削除します。 |
|
( x -- x x ) |
一番上のスタック項目を複製します。 |
|
nip |
( x1 x2 -- x2 ) |
2 番目のスタック項目を削除します。 |
( x1 x2 -- x1 x2 x1 ) |
2 番目のスタック項目をスタックの一番上にコピーします。 |
|
( xu ... x1 x0 u -- xu ... x1 x0 xu ) |
u 番目のスタック項目をコピーします (1 pick = over)。 |
|
( -- x ) ( R: x -- ) |
復帰スタック項目をスタックに転送します。 (使用には注意が必要です。) |
|
( -- x ) ( R: x -- x ) |
復帰スタックの一番上をスタックにコピーします。 |
|
( xu ... x1 x0 u -- xu-1 ... x1 x0 xu ) |
u 箇のスタック項目を回転します(2 roll = rot)。 |
|
( x1 x2 x3 -- x2 x3 x1 ) |
3 つのスタック項目を回転します。 |
|
( x1 x2 -- x2 x1 ) |
一番上の 2 つのスタック項目を入れ換えます。 |
|
( x1 x2 -- x2 x1 x2 ) |
一番上のスタック項目を 2 番目の項目の下にコピーします。 |
代表的なスタック操作の用途は、次の例に示すように、すべてのスタック項目を保持しておきながら、一番上のスタック項目を表示することかも知れません。
5 77 ok dup (一番上のスタック項目を複製) 5 77 77 ok . (一番上のスタック項目を削除し、表示) 77 5 77 ok |
Forth は、新しいコマンドワードの利用者定義を作成するための簡単な手段を提供します。表 4-3に、利用者定義作成用の Forth ワードを示します。
表 4-3 コロン定義ワード
コマンド |
スタックダイアグラム |
説明 |
---|---|---|
( -- ) |
ワード new-name の新しいコロン定義を開始します。 |
|
( -- ) |
コロン定義を終了します。 |
新しいコマンドの定義は、: 用いて定義することから、コロン定義と呼ばれます。たとえば、任意の 4 つの数値を加算し、結果を表示する新しいワード add4 を作成するとします。定義は、たとえば次のように作成できます。
ok : add4 + + + . ; ok |
; (セミコロン) は、(+ + + .)の操作を行わせるように add4 を定義する定義の終わりを示します。3 つの加算演算子 (+) は 4 つのスタック項目を 1 つの和に変えてスタックに残します。次に . はその結果を削除し、表示します。次に例を示します。.
ok 1 2 3 3 + + + . 9 ok 1 2 3 3 add4 9 ok |
これらの定義はローカルメモリーに格納されます。つまり、システムをリセットすると消去されるということです。よく使う定義を保存するには、(オペレーティングシステムのもとでテキストエディタを使用して、または NVRAMRC エディタを使用して) テキストファイルにそれらの定義を保存します。このテキストファイルは、以降、必要に応じて読み込めます。(ファイルの読み込みについての詳細は、第 5 章「プログラムの読み込みと実行」を参照してください。)
ユーザーインタフェースから定義を入力すると、: (コロン) を入力してから ; (セミコロン) を入力するまで、ok プロンプトが ] (右角括弧) プロンプトになります。たとえば、add4 の定義は次のように入力できます。
ok : add4 ] + + + ] . ] ; ok |
(テキストファイル内に) 作成するすべての定義には、そのスタック効果がない場合 (--) であっても、それぞれに、定義が表すスタック効果のダイアグラムが必要です。スタックダイアグラムは、ワードの正しい使い方を示すのできわめて重要です。さらに、複雑な定義内にはわかりやすいスタックコメントを使用してください。それによって、実行のフローを容易に追跡できます。たとえば、add4 を作成するには、次のように定義できます。
: add4 ( n1 n2 n3 n4 -- ) + + + . ; |
または、次のようにも定義できます。
: add4 ( n1 n2 n3 n4 -- ) + + + ( sum ) . ; |
「( 」(左側括弧) は、それ以降「 )」 (右側括弧) までのテキストを無視することを意味します。ほかのすべての Forth ワードと同様に、左側括弧の右側には 1 つまたはそれ以上の空白文字が必要です。
表 4-4 に示すコマンドは、データスタック上の項目に対する基本演算コマンドです。
表 4-4 演算機能
ユーザーインタフェースはメモリー内容の確認および設定用のコマンドを備えています。次の操作はユーザーインタフェースを使用して行います。
任意の仮想アドレスの読み取り、書き込み。
メモリー演算子を使用すると、任意のメモリー位置からの読み取り、任意のメモリー位置への書き込みが行えます。以降の例に示すメモリーアドレスはすべて仮想アドレスです。
8 ビット、16 ビット、32 ビットのさまざまな操作ができます。一般的に、c (文字) という接頭辞は 8 ビット (1 バイト) の操作を示し、w (ワード) という接頭辞は 16 ビット (2 バイト) の操作を示し、l (quadlet) という接頭辞は 32 ビット (4 バイト) の操作を示します。
l は、場合によっては、数字の 1 との混同を避けるために大文字で示すことがあります。
waddr 、qaddr、addr64 は境界の制約をもつアドレスを示します。たとえば、qaddr は 32 ビット (4 バイト) 境界を示し、したがってそのアドレス値は次の例に示すように 4 で割り切れなければなりません。
ok 4028 L@ ok 4029 L@ Memory address not aligned ok |
OpenBoot に実装されている Forth インタプリタはできるだけ ANS の Forth 標準に準拠しています。明示的に 16 ビットまたは 32 ビットを取り出す場合は、@ の代わりにそれぞれ w@ または L@を使用してください。そのほかのメモリーやデバイスレジスタへのアクセスコマンドもこの規則に従います。
表 4-5 にメモリーアクセス用のコマンドを示します。
表 4-5 メモリーアクセスコマンド
dump コマンドは特に便利です。このコマンドは、メモリーの領域をバイト値、ASCII 値の両方で表示します。次の例は、仮想アドレス 10000 からの 20 バイトを表示します。さらに、特定のメモリー位置に読み書きする方法も示しています。
(たとえば、@ を使用して) 無効なメモリー位置をアクセスしようとした場合は、処理はただちに終了し、PROM が Data Access Exception または Bus Error などのエラーメッセージを表示します。(注 : 数字は 16 進数で記述されています。)
表 4-6にメモリー割り当て用のコマンドを示します。
表 4-6 メモリー割り当てコマンド
次の画面は alloc-mem と free-mem の使用例です。
alloc-mem が 4000 バイトのメモリーを割り当てます。その予約領域の開始アドレス (ffef7a48) が表示されます。
dump が ffef7a48 から始まるメモリー 20 バイトの内容を表示します。
次に、このメモリー領域を値 55でみたします。(注 : ここで数字は 16 進数で記述されています。)
最後に、free-mem が割り当てられた ffef7a48 からの 4000 バイトのメモリーを返します。
ok ok 4000 alloc-mem . ffef7a48 ok ok ffef7a48 constant temp ok temp 20 dump 0 1 2 3 4 5 6 7 ¥/ 9 a b c d e f 01234567v9abcdef ffef7a40 00 00 f5 5f 00 00 40 08 ff ef c4 40 ff ef 03 c8 ..u_..@..oD@.o.H ffef7a50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ ffef7a60 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ ok temp 20 55 fill ok temp 20 dump 0 1 2 3 4 5 6 7 ¥/ 9 a b c d e f 01234567v9abcdef ffef7a40 00 00 f5 5f 00 00 40 08 55 55 55 55 55 55 55 55 ..u_..@.UUUUUUUU ffef7a50 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 UUUUUUUUUUUUUUUU ffef7a60 55 55 55 55 55 55 55 55 00 00 00 00 00 00 00 00 UUUUUUUU........ ok ok temp 4000 free-mem ok |
memmap の使用例を次に示します。
ok 200.0000 sbus 1000 memmap ( virt ) ok |
ここでは、システムに依存するデバイスアドレスを知る必要なしに、ok プロンプトから SBus デバイスを割り当てる一般的な方法を示します。この方法は、SBus デバイス上に有効な FCode PROM があるかどうかには依存しません。2.0 またはそれ以降のバージョンのすべての OpenBoot システムで有効です。
ok " /sbus" select-dev ok (offset) (slot#) (size) map-in ( virt ) ok |
たとえば、システムのスロット番号 3 のデバイスの FCode PROM の内容を調べるには、次のように入力します。
ok " /sbus" select-dev ok 0 3 1000 map-in .s ffed3000 ok dup 20 dump ( FCode PROM の内容が最初の 20 バイト表示されます) ok |
この方法は、多少変更することができます。
システムによっては、システムの SBus のパス名が異なることがあります。たとえば、" /iommu/sbus" (Sun4m の場合)や " /io-unit/sbi" (Sun4d の場合) です。ok プロンプトで show-devs コマンド (すべてのシステムデバイスをリストします) を使用するのが、正しいパスを知る 1 つの方法です。
スタックに (オフセットサイズを) 直接入れる方法は、将来のシステムの一般的ケースでは有効である場合とそうでない場合があります。問題が生じた場合は、さらに一般的な手段として、次の方法を試みてください。
ok " /sbus" select-dev ok " 3,0: decode-unit ( offset space ) ok 1000 map-in ( virt ) ok |
辞書には用意されているすべての Forth コマンドが含まれています。ワード定義を使って新しい Forth コマンドを作成します。
ワード定義は 2 つのスタックダイアグラムを必要とします。最初のダイアグラムでは、新しいコマンドを作成するときのスタック効果を示します。第 2番目の (Usage:) ダイアグラムはそのコマンドが後で実行されるときのスタック効果を示します。
表 4-7 に辞書エントリを作成するためのワード定義を示します。
表 4-7 ワード定義
ワード定義 constant を使用して、値が変わらない名前を作成できます。単純なコロン定義 : foo 22 ; でも同じ結果になります。
ok 72 constant red ok ok red . 72 ok |
value では任意の数値に名前を付けることができます。その名前を後で実行すると、その代入値がスタックに残されます。次の例は 22 という値を foo という名前のワードに代入し、次に foo を呼び出してその代入値を演算に使用します。
ok 22 value foo ok foo 3 + . 25 ok |
値は辞書コンパイルワード is で変更できます。たとえば、次の例を参照してください。
ok 43 value thisval ok thisval . 43 ok 10 to thisval ok thisval . 10 ok |
value を使用して作成したコマンドは、数値が必要な場合、@ を使用しないで済むので便利です。
ワード定義 variable は 32 ビットのメモリー領域に名前を割り当てます。この領域は必要に応じて値の保存用として使用できます。後でその名前を実行すると、領域のメモリーアドレスがスタックに残されます。一般的に、そのアドレスの読み書きには @ と ! が使用されます。次の例を参照してください。
ok variable bar ok 33 bar ! ok bar @ 2 + . 35 ok |
ワード定義 defer により、必要に応じて異なる機能を読み込むスロットが生成されるので、後から機能を変更できるコマンドを生成できます。次の例を参照してください。
ok hex ok defer printit ok ['] .d to printit ok ff printit 255 ok : myprint ( n -- ) ." It is " .h ] ." in hex " ; ok ['] myprint to printit ok ff printit It is ff in hex ok |
「辞書」 にはシステムが備えているすべての Forth コマンドが含まれています。表 4-8 に辞書検索用のツールを示します。
表 4-8 辞書検索コマンド
see thisword の形式で使用した場合、see は指定されたコマンドを逆コンパイルします (つまり、thisword を作成するための定義を表示します)。メモリースペースを節減するために内部の名前が PROM のシンボルテーブルから省略されていることがあるため、逆コンパイル結果の定義はときとして不明確なものになる場合があります。
次の画面は sifting の使用例です。
ok sifting input input-device input restore-input line-input input-line input-file ok |
words は、辞書内のすべてのワード (コマンド) 名を最新の定義から先に表示します。
表 4-9 に、データを辞書へコンパイルするためのコマンドを示します。
表 4-9 辞書コンパイルコマンド
表 4-10 にスタック値表示用の基本コマンドを示します。
表 4-10 基本数値表示
.s コマンドはスタックの内容全体をそのまま表示します。このコマンドはいつでもデバッグ目的に使用して安全です。(これは、showstack が自動的に実行する機能です。)
表 4-11のコマンドを使用して、現在使用している数値の基数を変更できます。
表 4-11 基数の変更
d# 、h# 、o# の各コマンドは、現在の基数を明示的に変更しないで、特定の数値を別の基数で入力するときに便利です。
ok decimal (基数を 10 進に変更) ok 4 h# ff 17 2 4 255 17 2 ok |
.d および .h コマンドの機能は、現在の基数設定にかかわりなく、値をそれぞれ 10 進または 16 進で表示する点を除いて、「.」と同じです。次の例を参照してください。
ok hex ok ff . ff .d ff 255 |
この節ではテキストの入出力用コマンドについて説明します。これらのコマンドは文字列や文字配列を制御し、ユーザーからのコメント入力およびキーボードの走査制御を可能にします。
表 4-12 にテキスト入力制御用のコマンドを示します。
表 4-12 テキスト入力制御
コメントは、コードの機能を記述するために、(一般的にテキストファイル内の) Forth ソースコードに使用します。( (左側括弧) がコメントを開始する Forth ワードです。右側括弧 ) の前までの文字はすべて、Forth インタプリタが無視します。スタックダイアグラムは ( を使用するコメントとして取り扱われます。
( の後に空白文字を入れることを忘れないでください。それによって、( は Forth ワードとして認識されます。
¥(バックスラッシュ) はテキスト行末でコメントが終わりになることを示します。
key はキーが押されるまで待ち、押されると、そのキーの ASCII 値をスタックに返します。
ascii は、ascii x の形式で使用され、文字 x の数字コードをスタックに返します。
key? はキーボードを走査して、ユーザーが新たになんらかのキーを押したかどうかを調べ、フラグをスタックに返します。つまり、キーが押されていた場合は true を、押されていない場合は false を返します。フラグの使い方については、「条件フラグ」 の説明を参照してください。
表 4-13 に汎用のテキスト表示用コマンドを示します。
表 4-13 テキスト出力表示
cr はキャリッジリターン文字を出力に送ります。次の例を参照してください。
ok 3 . 44 . cr 5 . 3 44 5 ok |
emit は ASCII 値がスタックにある英字を表示します。
ok ascii a 61 ok 42 61 42 ok emit emit Ba ok |
表 4-14 にテキスト文字列操作用のコマンドを示します。
表 4-14 テキスト文字列操作
一部の文字列操作コマンドは、アドレス (それらの文字があるメモリー内の位置) と長さ (文字列の文字数) を指定します。その他のコマンドは、パックされた文字列、または長さを表すバイトを格納するメモリー位置である pstr とその後の一連の文字を使用します。コマンドのスタックダイアグラムは、どの形式が使用されるかを示します。たとえば、count はパックされた文字列を addr-len (アドレスと長さの組み合わせ) 文字列に変換します。
コマンド ." は ." string" の形式で使用します。このコマンドは必要なときにテキストを出力します。"(二重引用符) はテキスト文字列の終わりを示します。次の例を参照してください。
ok : testing 34 . ." This is a test" 55 . ; ok ok testing 34 This is a test55 ok |
通常、システムはすべてのユーザー入力にキーボードを、また大部分の表示出力にディスプレイ画面付きのフレームバッファーをそれぞれ使用します。(サーバーシステムはシステムのシリアルポートに接続された ASCII 端末を使用できます。システム本体への端末の接続についての詳細は、システムのインストールマニュアルを参照してください。) 入力先、出力先、それらの両方をシステムのいずれかのシリアルポートに変更できます。これは、たとえばフレームバッファーのデバッグ時に便利です。
表 4-15 に入出力先変更用のコマンドを示します。
表 4-15 入出力先の変更用コマンド
コマンド |
スタックダイアグラム |
説明 |
---|---|---|
input |
( device -- ) |
入力用のデバイス (keyboard または device-specifier) を選択します。 |
io |
( device -- ) |
入出力用のデバイスを選択します。 |
output |
( device -- ) |
出力用のデバイス (screen または device-specifier) を選択します。 |
input および output コマンドは、それぞれ、現在の入力および出力用デバイスを一時的に変更します。変更はコマンドの入力時に行われます。システムをリセットする必要はありません。システムリセットまたは電源再投入を行うと、入出力デバイスは NVRAM システム変数 input-device と output-device に指定されているデフォルト設定に戻ります。これらの変数は、必要に応じて変更できます (デフォルトの変更についての詳細は、第 3 章「システム変数の設定」を参照してください)。
input の前には、keyboard、ttya、ttyb または device-specifier テキスト文字列のうちのどれか 1 つを入れます。たとえば、入力が現在キーボードから受け入れられていて、入力がシリアルポート ttya に接続されている端末から受け入れられるように変更する場合、次のように入力します。
ok ttya input ok |
この時点で、キーボードは (Stop-A 以外は) 機能しなくなりますが、ttya に接続されている端末から入力されるテキストはすべて入力として処理されるようになります。すべてのコマンドが通常どおりに実行されます。
キーボードを再び入力デバイスとして使用するには、端末のキーボードを使用して次のように入力します。
ok keyboard input ok |
同様に、output の前にも screen、ttya、または ttyb のどれか 1 つを入れます。たとえば、通常のディスプレイ画面でなく、ttya に出力を送りたい場合は、次のように入力します。
ok ttya output |
通常のディスプレイ画面は応答の ok プロンプトを表示せず、ok プロンプトも以降のすべての出力も ttya に接続されている端末に表示されます。
io も、入出力の両方を指定した場所に変更する点以外、同じ方法で使用されます。
一般的に、input、output、io には device-specifier を指定する必要があります。device-specifier はデバイスパス名、デバイスの別名のどちらでもかまいません。次の 2 つの例に示すように、デバイスは、二重引用符 (") を使用して Forth の文字列として次のように指定する必要があります。
ok " /sbus/cgsix" output |
または、次のように指定します。
ok " screen" output |
上記の 2 つの例では 、ttya、screen、 keyboard は Forth のワードであり、いずれも、それぞれの対応のデバイス別名文字列をスタックに入れます。
OpenBoot では、ユーザーインタフェース用として (一般的なテキストエディタである EMACS のような) 使用するコマンド行エディタ、一部のオプション拡張、オプションの履歴機を指定しています。これらの強力なツールを使用して、前のコマンドを入力し直さないで再び実行して、現在のコマンド行を編集して入力エラーを修正し、前のコマンドを呼び出すことができます。
表 4-16に、ok プロンプト時に使用できる行編集用のコマンドを示します。
表 4-16 コマンド行エディタ用必須キー操作コマンド
操作キー |
説明 |
---|---|
Delete |
1 つ前の文字を消去します。 |
Backspace |
1 つ前の文字を消去します。 |
Control-U |
現在の行を消去します。 |
Return (Enter) |
現在の行の編集を終了し、表示されている 1 行全部をインタプリタに渡します。 |
OpenBoot 標準でも、これらの機能の 3 つの拡張グループを記述しています。表 4-17に、コマンド行編集用の拡張グループを示します。
表 4-17 コマンド行エディタ用オプションキー操作コマンド
操作キー |
説明 |
---|---|
Control-B |
1 文字位置戻ります。 |
Escape B |
1 語後戻ります。 |
Control-F |
1 文字位置進みます。 |
Escape F |
1 語進みます。 |
Control-A |
行の始めまで戻ります。 |
Control-E |
行の終わりに進みます。 |
Delete |
前の 1 文字を消去します。 |
Backspace |
前の 1 文字を消去します。 |
Control-H |
前の 1 文字を消去します。 |
Escape H |
語の初めからカーソルの直前まで消去し、消去した文字を保存バッファーに格納します。 |
Control-W |
語の初めからカーソルの直前まで消去し、消去した文字を保存バッファーに格納します。 |
Control-D |
カーソル位置の文字を消去します。 |
Escape D |
カーソル位置から語の終りまで消去し、消去した文字を保存バッファーに格納します。 |
Control-K |
カーソル位置から行の終りまで消去し、消去した文字を保存バッファーに格納します。 |
Control-U |
1 行を全部消去し、消去した文字を保存バッファーに格納します。 |
Control-R |
1 行を表示しなおします。 |
Control-Q |
次の文字の前に引用符を付けます (制御文字を挿入できます)。 |
Control-Y |
保存バッファーの内容をカーソル位置の前に挿入します。 |
コマンド行履歴の拡張機能として、前に入力したコマンドを EMACS のようなコマンド履歴バッファーに保存できます。このバッファーは 8 エントリ以上入れることができます。保存したコマンドは、バッファー内を前後に移動することにより、再び呼び出すことができます。呼び出したコマンドは、編集したり、(Return キーを押して) 再びインタプリタに渡すことができます。表 4-18 にコマンド行履歴拡張用のキーを示します。
表 4-18 コマンド行履歴用オプションキー操作コマンド
操作キー |
説明 |
---|---|
Control-P |
コマンド履歴バッファー内の 1 行前のコマンド行を表示します。 |
Control-N |
コマンド履歴バッファー内の次のコマンド行を表示します。 |
Control-L |
Dコマンド履歴バッファー全てを表示します。 |
コマンド名補完機能では、ワードのすでに入力した部分に基づいて辞書から 1 つまたはそれ以上の一致するワードを検索して、長い Forth のワード名を補完します。ワードの一部を入力した後にコマンド補完キー操作として Control-Space を押すと、システムは次のように応答します。
システムが一致ワードを 1 つだけ検索した場合は、ワードの未入力部分が自動的に表示されます。
システムは、一致候補を複数検索した場合は、すべての候補のすべての共通文字を表示します。
システムは、入力した文字に一致する文字を検索できなかった場合は、残りの文字との一致が 1 つ以上現れるまで、右側から文字を削除します。
システムは、正しい候補を判定できない場合は、警報音を鳴らします。
表 4-19にコマンド補完拡張用のキーを示します。
表 4-19 コマンド補完用オプションキー操作コマンド
操作キー |
説明 |
---|---|
Control-Space |
現在のワードの名前を補完します。 |
Control-? |
現在のワードのすべての一致候補を表示します。 |
Control-/ |
現在のワードのすべての一致候補を表示します。 |
Forth の条件付き制御コマンドはフラグを使用して真/偽の値を示します。フラグは、テスト基準に基づいて、いくつかの方法で生成できます。生成できたら、ワード "." でスタックから表示したり、条件付き制御コマンドの入力として使用できます。条件付き制御コマンドは、フラグが真 (true) の場合と偽 (false) の場合にそれぞれ異なる応答を表示します。
表示値が 0 の場合は、フラグの値が falseであることを示します。-1 またはその他のゼロ以外の任意の数値は、フラグが trueであることを示します。(16 進では、値 -1 は ffffffff として表示されます。)
表 4-20 に、比較テストを実行し、trueまたは falseフラグの結果をスタックに残すコマンドを示します。
表 4-20 比較コマンド
> はスタックから 2 つの数値を取り出し、最初の数値が 2 番目の数値より大きかった場合は true (-1) をスタックに返し、そうでなかった場合は false (0)を返します。次に例を示します。
ok 3 6 > . 0 (3 は 6 より大きくない) ok |
0= はスタックから 1 項目を取り出し、その項目が 0 であった場合は true を返し、そうでなかった場合は false を返します。このワードはどちらのフラグもその反対の値に反転します。
以降の各項では、Forth プログラム内の実行フローの制御用のワードについて説明します。
if、then、else の各コマンドは組み合わされて単純な制御構造を作ります。
表 4-21に条件付き実行フロー制御用のコマンドを示します。
表 4-21 if...else...then コマンド
コマンド |
スタックダイアグラム |
説明 |
---|---|---|
if |
( flag -- ) |
flag が true の場合、このコマンドの後のコードを実行します。 |
else |
( -- ) |
if が false の場合、このコマンドの後のコードを実行します。 |
then |
( -- ) |
if...else...then を終了します。 |
これらのコマンドの書式は次のとおりです。
flag if (true の場合これを実行) else (false の場合これを実行) then (通常どおりに実行を継続) |
または
flag if (true の場合これを実行) then (通常どおりに実行を継続) |
if コマンドはスタックからフラグを 1 つ「消費」します。そのフラグが true (ゼロ以外) であれば、if の後のコマンドが実行されます。true でなければ、(存在する場合) else の後のコマンドが実行されます。
ok : testit ( n -- ) ] 5 > if ." good enough " ] else ." too small " ] then ] ." Done. " ; ok ok 8 testit good enough Done. ok 2 testit too small Done. ok |
] プロンプトは、それが現れる間は、新しいコロン定義の作成の途中であることをユーザーに示します。このプロンプトはセミコロンを入力して定義を終了すると ok に戻ります。
高水準の case コマンドであり、複数の候補のなかから代替実行フローを選択するために用意されています。このコマンドの方が、深く入れ子になった ifthen コマンドよりも読みやすいという利点があります。
表 4-22に条件付き case コマンドを示します。
表 4-22 case文コマンド
case コマンドの使用例を示します。
ok : testit ( testvalue -- ) ] case 0 of ." It was zero " endof ] 1 of ." It was one " endof ] ff of ." Correct " endof ] -2 of ." It was minus-two " endof ] ( default ) ." It was this value: " dup . ] endcase ." All done." ; ok ok 1 testit It was one All done. ok ff testit Correct All done. ok 4 testit It was this value: 4 All done. ok |
(省略可能な) default 句はまだスタックにあるテスト値を使用できますが、その値を削除してはなりません ( . でなく dup .を使用してください)。 of 句が正常に実行されれば、テスト値はスタックから自動的に削除されます。
begin ループは、特定の条件が満たされるまで、同じコマンドの実行を繰り返します。そのようなループのことを条件付きループといいます。
表 4-23に条件付きループの実行制御用のコマンドを示します。
表 4-23 begin(条件付き) ループコマンド
次に 2 つの一般的な形式を示します。
begin any commands... flag until |
および
begin any commands... flag while more commands repeat |
上記の両方の場合とも、所定のフラグ値によってループが終了させられるまで、ループ内のコマンドが繰り返し実行されます。ループが終了すると、通常、実行はループを閉じているワード (until または repeat) の後のコマンドに継続されます。
beginuntil の場合は、until がスタックの一番上からフラグを削除してそれを調べます。フラグが false の場合は、実行は beginのすぐ後に引き継がれて、ループが繰り返されます。フラグの trueの場合は、実行はループから抜け出ます。
beginwhilerepeat の場合は、while がスタックの一番上からフラグを削除して調べます。フラグが true の場合は、whileのすぐ後のコマンドが実行されてループが繰り返されます。repeatコマンドは制御を自動的に begin に戻してループを継続させます。while が現れたときにフラグが false であった場合は、実行はただちにループから抜け出し、制御がループを閉じている repeat の後の最初のコマンドに移ります。
これらのループのいずれについても、「true ならば通り過ぎる」と覚えると忘れないでしょう。
次に簡単な例を示します。
ok begin 4000 c@ . key? until (任意のキーが押されるまで繰り返す) 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 ok |
この例では、ループはまずメモリー位置 4000 から 1 バイトを取り出して表示します。次に、key? コマンドが呼び出され、これが、ユーザーがそれまでにどれかのキーを押していれば true をスタックに残し、そうでない場合は false を残します。このフラグはuntil によって「消費」され、その値が false であった場合は、ループが繰り返されます。キーを押せば、次に呼び出されたとき、key? は true を返し、したがってループは終了します。
Forth の多くのバージョンとは異なり、ユーザーインタフェースの場合は、ループや条件付き制御構造を対話的に使用できます。つまり、まず最初に定義を作成する必要がありません。
do ループ (カウント付きループとも呼ばれます) は、ループの繰り返し回数があらかじめ計算できるときに使用します。do ループは、通常、指定した終了値に達する直前に終了します。
表 4-24に カウント付きループの実行制御用コマンドを示します。
表 4-24 do (カウント付き)ループコマンド
次の画面で、ループの使用方法をいくつか示します。
表 4-25に、前記以外のプログラム実行制御用のコマンドについて説明します。
表 4-25 プログラム実行制御コマンド
abort はプログラムの実行を即時に終了させ、制御をキーボードに返します。abort" は 2 点を除いて abort と同じです。第 1 点は、フラグが true の場合にスタックからフラグを削除し、その後は何もしないで強制終了させることです。もう 1 点は、強制終了が行われたとき、なんらかのメッセージを表示することです。
eval は (アドレスと長さにより指定された) 文字列をスタックから取り出します。次に、キーボードから入力される場合と同様に、その文字列の文字が解釈されます。Forth のテキストファイルをメモリーに読み込んでいる (第 5 章「プログラムの読み込みと実行」を参照) 場合は、eval を使用してそのファイル内の定義をコンパイルできます。