FORSE
FORSE
TANACOM-1がソフトウェア黎明期を脱出し、本格的なコンピューターに変身していくのは、システムプログラムFORSEを産み出す事が必要でした。
システムプログラムFORSEとは
システムプログラムFORSEは、FORSE言語のインタープリタ、キーボード、キャラクタディスプレー、カセットインターフェース等の入出力の制御、エディタで構成されます。
これらは、今でいうOS(オペレーティングシステム)の役目をしていました。
1978年当時、一般的なマイコンの場合、外部記憶装置として、カセットテープレコーダーが主流であり、まだフロッピーディスク、ハードディスクなどのマイコン用は存在していませんでした(ミニコンやオフィスコンピュータ向けはあった)。よって、マイコン用Disk Operation Systemも開発途上であり、一般には存在していませんでした。
FORSE言語とは
FORSE とは、TANACOM-1 上で動くVTL( Very Tiny Language )系の小型言語です。
当時FORTHというプログラム言語に興味があったので、FORTH風にしようと
- スタックマシン
- 逆ポーランド記法
そして、VTLの特徴である、記号言語
今で言う「俺言語」です。
FORSEの誕生前夜
マイコン雑誌に初めて、VTL (Very Tiny Language) の記事が登場した時は、とても衝撃的でした。
モトローラ製8bitマイクロプロセッサーMC6800を搭載したマイコン「ALTAIR 680b」用に生み出された小型言語がそのVTLであり、インタープリタの大きさは何と 786 byteだったのです。
VTLは記号言語とも呼ばれ、プログラム命令が「 ! # $ % & ' ( ) = - + * : ; ? / > < , [ ] 」などの記号を使い、1記号1記号にそれぞれ意味を持たせてありました。
このVTLをヒントにその後、色々な小型言語インタープリタが色々なマイコン上に登場しました。
1978年当時マイコン雑誌では、その新しい小型言語インタープリタの記事で賑わっていました。
その頃はTANACOM-1には、VTLの様なもは存在していませんから、プログラムを一生懸命ハンドアセンブルして、モニタープログラムから16進(Hex)コードしこしこと打ち込むしかなかったのです。
そこで、意を決して、TANACOM-1用を作る事に挑戦しました。
FORSE言語 詳細
FORSE言語の特徴
①goto命令を持たない、完全な構造化言語である
②スタックオリエンテッド・マシンである
FORSE言語の仕様
注.以降『 』、~、△は説明をするため用いた表現であり、FORSE制御文字ではない。
注.黄色の四角枠の中では、例を説明する
■ FORSEの実行
アプリケーションの先頭の行から下へ評価されていく。
1行の中では、左から右へ評価されていく。
■ スタック
計算用スタックとコール用スタックを持つ。
但し、コール用スタックは、直接制御することは出来ない。
以下、スタックとは、計算用スタックを指す。
■ 真(true)と偽(false)
FORSEでは、ゼロ(0)を偽(false)、1もしくは非ゼロ(非0)を真(true)とする
◆ セパレータ
スペースとカンマ『,』はセパレータであり、10進数値の前後に置き、区別のために使用する
『1234』 は、『1234』
『12 34』は、『12』と『34』
『12,34』は、『12』と『34』
◆ 数値 符号付き16bit整数 -32768 ~ 32767
◆ 定数
10進定数 nnnnn (プログラム中には、マイナス記号を頭に付けた負の定数を書くことは出来ない)
16進定数 $nnnn
スタックアクション:その定数値をスタックトップに置く
100 ・・・ 100 をスタックトップに置く
$1B2F ・・・ $1B2F をスタックトップに置く
◆ 変数
変数名は1文字で表現される、A~Zの26種。
変数は、int型であり、全てグローバル変数である
スタックアクション:その値をスタックトップに置く
C ・・・ 変数Cの値をスタックトップに置く
◆ 代入文
代入演算子は『 :変数名 』と表記する( 記号文字はコロン )
スタックアクション:スタックトップから取り出し、その変数へ代入する
コロンと変数名の間は、空けないで下さい
100 :A 100 を A に代入
C :F C の内容を F に代入
◆ 変数の間接表現
間接演算子は『 ;変数名 』と表記する( 記号文字はセミコロン )
その変数の持つ値とインデックスを加算した値をアドレスして、メモリを読み書きする。
これにより、配列としても使えるし、メモリへのpeek、poke命令としても使える。
$8081 :Z としておき、
4;Z なら、$8085番地のメモリの値をスタックトップに置く( peek命令と同じ )
代入演算子と組み合わせると、
$8081 :Z としておき、
38,0:;Z なら、38を$8081番地のメモリへ書き込む( poke命令と同じ )
72,4:;Z なら、72を$8085番地のメモリへ 〃
◆ 演算
- 『 + 』 加算。スタックのセカンドとトップを足し、答えをトップに置く
- 『 - 』 減算。スタックのセカンドからトップを引き、答えをトップに置く
- 『 * 』 乗算。スタックのセカンドとトップを掛け、答えをトップに置く
- 『 / 』 除算。スタックのセカンドをトップで除し、答えをトップに置く
- 『 % 』 剰余。スタックのセカンドをトップで除し、余りをトップに置く
- 『・ 』 絶対値( 記号文字は真ん中点 )。スタックトップの絶対値をトップに戻す
- 『 ' 』 乱数。スタックトップを取り出し、0~その値までのランダムな値をトップに戻す。
◆ 比較演算
- 『 = 』 スタックのセカンドとトップを比較し、等しければ『1』を、そうでなければ『0』をトップに置く
- 『 <> 』 スタックのセカンドとトップを比較し、等しくなければ『1』を、そうでなければ『0』をトップに置く
- 『 > 』 スタックのセカンドがトップより大きければ『1』を、そうでなければ『0』をトップに置く
- 『 < 』 スタックのセカンドがトップより小さければ『1』を、そうでなければ『0』をトップに置く
◆ 論理演算
- 『 ロN 』 AND。スタックのセカンドとトップのANDしたものをトップに置く
- 『 ロO 』 OR。スタックのセカンドとトップのORしたものをトップに置く
◆ スタック関数
- 『 . 』 コピー( 記号文字はピリオド )。スタックトップをコピーしたものをトップに置く
- 『 ロS 』 スワップ。スタックのセカンドとトップを入れ替える
- 『 ロB 』 バイトストア。スタックトップをbyteアドレスとし、セカンドの内容をその位置に書き込む
- 『 ロL 』 バイトロード。スタックトップをbyteアドレスとし、その位置にある値を読み、トップに置く
◆ キーボード入力に関するもの
- 『 ? 』 10進入力。符号付き10進整数を入力し、スタックトップに置く
- 『 ロ? 』 1文字入力。1文字入力し、その文字コードをスタックトップに置く
- 『 ロG 』 GetKey。この瞬間、キーが押されてなければ0を、押されていればその文字コードをスタックトップに置く
- 『 ロH 』 16進4桁入力。16進数4桁を入力し、スタックトップに置く
◆ ディスプレー出力に関するもの
- 文字列出力 ダブルクォーテーションで囲まれた文字列をディスプレー表示する。
"abcdef" は、ディスプレーに abcdef と表示される
- 『 \ 』 改行出力。1回改行する
- 『 ロE 』 画面クリア。画面を消去
- 『 ロK 』 カーソル位置セット。スタックのセカンドをX座標、トップをY座標として、そのXY位置にカーソルを移動する
- 『 : ? 』 10進出力。スタックトップを取り出し、符号付き10進整数で表示する
コロンと?の間は、空けないで下さい - 『 ロC 』 1文字出力。スタックトップを取り出し、その値を文字コードとして、その文字を表示する
- 『 ロ$ 』 16進出力。スタックトップを取り出し、16進4桁で表示する
◆ 特殊関数
- 『 & 』 マシン語コール。スタックトップをアドレスとして取り出し、そのアドレスから始まるマシン語をコールする
- 『 ロT 』 時間調整。スタックトップをタイマーコードとして取り出し、コードに応じた時間だけ、処理を止め待機する
■ 制御構文
◆ if文
- 『 ( ~ ) 』。スタックトップを取り出し、真(true)であれば、『~』の部分を実行する。
◆ if~else文
- 『 ( ~ )「 △ ) 』。スタックトップを取り出し、真(true)であれば、、『~』の部分を実行し、偽(false)であれば、『△』の部分を実行する。
◆ While~Do文
- 『 [ 条件式 # ~ 0] 』。条件式が真(true)であれば、『~』の部分を実行し、ループを繰り返す。
◆ Repeat~Until文
- 『 [ ~ 条件式 ] 』。条件式が真(true)になるまで、『~』の部分の実行を繰り返す。
◆ END文
- 『 @ 』 END。FORSEの評価を終了し、何かキーが押されるまで、そのまま待機する
■ 一般関数
関数名は1文字で表現される、A~Z およびア~ンの72種
- 関数の呼び出し方法は、『 !関数名 』
- 関数の定義は『 Λ関数名 』で、始まり、『 0] 』のリターンで定義を終わる。
また、関数は、END文以降で定義すること
基本ソフト FORSE の使い方
基本ソフト FORSE の操作方法をご説明します。
FORSE が起動されると、常にFORSE エディトモード に入ります。
エディトモードの状態で、FORSE プログラムを書き、それから実行します。
また、書いたプログラムの修正、保存、読み出し等も行います。
エディトモードで使うコマンド
エディトモードでは、古典的なラインエディタが働いております(スクリーンエディタじゃないのです)。
プログラムテキストを行単位で、操作します。
先頭の行が「第0行」になります。
修正したい行があれば、行ポインタをそこに移動させ、修正すると云う操作の繰り返しになります。
エディトモードで使うコマンド
- 「E」・・・ プログラムの全消去
本当に消して良いか?の再確認はありまんので、要注意 - 「T n」・・・ 行ポインタを第n行に移動する
nが省略されると、トップ(第0行)に移動します。 - 「N n」・・・ 現在の行からn行先へ、行ポインタを移動する
nが省略されると、何もしません。 - 「D n」・・・ 現在の行からn行削除する
nが省略されると、何もしません。 - 「P n」・・・ 現在の行からn行をリスト表示する
但し、行ポイントタもリスト表示した分移動します。
nが省略されると、全リストを表示します。 - 「スペース xxx 」・・・ 現在の行の前に、内容を xxx とする新しい1行を追加
これを使ってどんどんプログラムを入力していきます。 - 「C」・・・ 現在の行を修正します。現在の行の内容がラインバッファにコピーされた後、1行入力状態になります。カーソルの「→」キーで従前の文章をなぞりながら、上書き修正出来ます。この1行内ではカーソルの「←」も使えます。但し、「↑↓」は使えません。
- 「@」・・・ プログラムの実行
- 「_ xxx 」・・・ その xxx をプログラムとして、この1行だけ、実行する
例 「_ 10 3 * 5 - :?」とすれば、答えの「25」が表示されます。 - 「M」・・・ プログラム領域の先頭byteアドレス、最終byteアドレス、現在の行の位置、空き領域の残量byte数を表示
注.ワードアドレスではなく、10 進表示でのbyteアドレスであるから、注意されたし
- 「R」・・・ カセットテープからプログラムを読み込む
( エミュレータでは、使用禁止です、暴走します) - 「W」・・・ カセットテープにプログラムを書き込む
( エミュレータでは、使用禁止です) - 「Q」・・・ カセットテープにマシン語とプログラムを書き込む
( エミュレータでは、使用禁止です) - 「Z n」・・・ メモリのnアドレスから、直接データ( 16進4桁)書込モードに突入
(使用は慎重に)
(ご注意点)
1.とにかく、全てが「正しいキーしか押されないだろう、よって、誤操作の監視はしない」という「性善説」で成り立ったおりますので、ご注意ください。
2.キー入力で、「ホーム」キーを押すと、画面消去されます。これは、実行中でのキー入力でも同様です。
3.FORSE プログラムを走らせて、最後にFORSE の終了命令「@」が実行されると、プログラムが停止し、静止画面状態で、待機します。
ここで、何かキーを押してやると、はじめて、エディトモードに戻ります。
但し、何かのキーが長押しになると、エディトモードへの次の入力キーと勘違されますので、ご注意下さい。
4.プログラムの実行中に、中断させたい場合、その方法は、次の1点だけてず。
① 数値や文字入力のため、カーソルが点滅して待っている状況に於いて、「 Bksp 」(バックスペースキー)を押すと、プログラムを中断して、エディトモードに戻ってくれます。
これ以外の場合は、中断する機能がありません。やむを得ず中断したい場合は、FORSE 自身を Re-Strat するのみです。
また、この「 Bksp 」(バックスペースキー)は、エディトモードの中でも有効です。 Bksp キーを押すと、編集している行から離脱出来ます(内容は更新されず、変更したモノは破棄されます)。
5.プログラムの保存と読み込みは、カセットテープにしか行えないので、エミュレータでは、別途の機能を持たせてあります。
これに関しては、「エミュレータの使い方」でご説明いたします。
FORSE言語 入門
FORSEは逆ポーランド法です。慣れると簡単です
C言語のプログラムで例えば
a=(b+1)/(c-7);
これをFORSEでは、次のように書きます。
まず、計算式を日本語で考えると、
bに1を足したものをcから7を引いたもので割りaに代入する、となります。
青の部分だけ取り出すと、
b1+ c7- / :a 代入のところだけ前後逆になりますが
これが同じ計算をするFORSEのプログラムです。
逆ポーランド法は日本語的に考えられるので、慣れると簡単です。
簡単なプログラム例
1から10までの総和を求めるプログラムでご説明いたします。
まずはC言語で書けば、
#include <stdio.h>
main()
{
int i , sum ;
sum=0 ;
for ( i = 1 ; i <= 10 ; i++ ) {
sum += i ;
}
printf( "SOUWA=%d\n" , sum ) ;
}
と、なります。
これを FORSE で書きますと、
0 :S 総和用変数Sを初期化
1 :I
[ SI+ :S I1+:I I 10 > ]
Iをインクリメントしながら10を越えるまで、繰り返す
”SOUWA=” SOUWA=と出力し
S :? ¥ 総和用変数Sを10進出力して、改行
@ おわる
FORSE のような記号言語は、読み辛いのが欠点ではありますが、プログラムを短く書けます。
FORSEは関数を再帰呼出しすることが出来ます。
Nの階乗を計算するプログラムを例として、ご説明します。
まずは、C言語で階乗を再帰呼出しで求めるには、
#include <stdio.h>
int kai(int n) 階乗を求める関数Kaiの定義
{
int k;
if ( n == 0 )
return 1;
k = kai( n - 1 ); kai()を再帰呼出し
return n * k ;
}
int main() メイン関数
{
int i, m;
scanf("%d", &i);
m = kai(i);
printf("%d!=%d.\n", i, m );
return 0;
}
と、なります。
これを、FORSE で記述すると、
? ¥
!K :?
@
ΛK
:N
N 0=( 1 )「 N N 1 - !K * )
0]
各行を説明しますと、
? ¥ 10進入力する。そして改行
!K :? 関数Kを呼び出し、求まった答えを10進出力する
@ ここでEND
ΛK ここから階乗を求める関数Kの定義
:N スタックより渡された引数を変数Nへ格納
N 0=( 1 )「 N N 1 - !K * )
Nが0だったら1を、そうでなければ、Nから1引いたもので関数Kをコールし、その答えと、積をとる。積はスタックトップに残る
0] リターン。定義終わり
FORSE は、グローバル変数それも 26 個しか持ちませんが、引数を渡すのにスタックが使えますので、この様な関数の再帰呼出しが、簡単に実現できます。