インタプリタ言語 STACK

平井真二
 
 STACKはS-OS“SWORD"上で動作する,4バイト型整数およびMAGICをサポートした簡易エディタつきのインタプリタ言語です。
 パラメータの受け渡しをスタックを通して行う点でかなりFORTHに似ていますが,元々は逆ポーランド記法のBASICもどきを作ろうという発想で生まれた言語です。そのため,GOTOやGOSUB〜RETURNがあり,逆にFORTHの特徴でもあるワードを作るという機能はありません。
 スタック領域を除いて8Kバイト弱の小さな言語ですが機能面でも速度でもFuzzy BASICには劣りません。セミコンパイラ使用時にはさらに1.5〜2倍ほど速くなります。
 

◆使用方法

 タイトル表示後,プロンプト']'が表示され,入カ待ちになります。]に続いてコマンド一覧のコマンドを入力するとそのコマンドが実行されます。また,1文字以上のスペースをつけてSTACKの命令を入力すると,その命令が実行されます(ダイレクトモード)。
例) ] 5 BELL
  ビープ音が5回鳴ります。
 エディタを使ううえでの注意点は,
1) テキストの入力において,I(追加),B(挿入)を使い分けなければならない。
2) nはラベルでなく,Tコマンドのリスト表示でエディタがつけた行番号である。
3) nは省略できない(Dコマンドの第2パラメータを除く)。
です。なお,コマンド中断にはSHIFT+BREAKを押してください。
 インタプリタでもそこそこのスピードはありますが,速度が要求される場合はセミコンパイラを使ってみてください。使い方は,テキストおよびスタックと重ならないアドレスを求めて,
 ] Cアドレス
と入力してください。実行は,
 ] Jアドレス
です(アドレスはCコマンドで入力した値と同じ)。セミコンパイラ使用時は,エラーチェックをほとんど行わないので,エラーが出ないことが確認されたテキストを用いてください。また,Jコマンドで実行したプログラムはBREAK文があるところ以外はSHIFT+BREAKはききません。
 

◆プ口グラムについて

 ワードの判別は,頭の1文字と文字列のASCIIコードの和の下位1バイトてチェックしています。この方法は,テーブルが小さくてすみ,プログラムも簡単ですが,ワード名を決めるのに苦労します。一応,ワードが追加できるようにはなってますが上記の理由により面倒ですのでわかる人はやってみてください。
 ラベルは1〜511まではテーブルを用いているので速いのですが,512以上はテキストの先頭からサーチするので遅くなっています。プログラムを書くときには気をつけてください。
 セミコンパイラの処理は,ワードの解析,定数とラベルの数値化,変数の格納アドレスのオフセットアドレスを求めるだけですので,実行レベルはインタプリタと同等です。スタックは空かどうかしかチェックしてません(速度優先)。
 そして,グラフィック関係のワードを使うには,あらかじめグラフイックパッケージ“MAGIC"をロードしておいてください。
 
表1 エディタコマンド一覧
I テキストエンドより追加入力を始める。SHIFT+BREAKでコマンド入力に戻る
Tn n行からテキストを茨木する。スペースで一時停止,SHIFT+BREAKでコマンド入力に戻る。なお,表示後のテキストはスクリーンエディット可能
Dn1,n2 n1からn2を削除する(,n2は省略可)
Bn n行からテキストの挿入を始める。SHIFT+BREAKでコマンド入力に戻る
S ファイル名 現在作成しているテキストをファイル名でセーブする
L ファイル名 ファイル名のァキストをロードする
Z アイレクトリを表示する
& 現在作成中のテキストを消去する
R テキストを復活する
P Tコマンドにおけるプリンタ出力のON/OFFを設定する。デフォルトはOFF
F 文字列 先頭から文字列を探し始める。スペースキーで一時停止.SHIFT+BREAKでコマンド入力に戻る
M 現在作成中のテキストの絡納されているアドレスを表示する
X アドレス テキスト格納先頭アドレスを指定する。デフォルトは4E00H番地。なお,アドレスは16進4桁で,また,初めて指定したときは必ず&を実行すること
! S-OSに戻る
G プログラムを実行する
C アドレス テキストをセミコンパイルする。結果はアドレス以降に格納される
J アドレス セミコンパイルしたプログラムを実行する。アドレスはCコマンドで指定したアドレスである
 
図 メモリマップ
3000H


33D3H


4684H

49A7H

4DA6H

4E00H






AA00H

AE00H
 
テキストエディタ
 
インタープリタ本体
 
ワークエリア
ラベルエリア
 
テキストエリア





パラメータスタック
リターンスタック
 

STACKリファレンスマニュアル

 <>内はスタックの状態で「──」の左が動作直前,右が動作直後の状態を表している。
 この中でdH,dL,d,c,addはそれぞれ4バイトデータの上位2バイト,4バイトデータの下位2バイト,2バイトデータ,2バイトデータの下位バイト,addはアドレス(2バイト)を示している。
 

■構文規則

・ラベルは1〜2047の範囲で必要なところだけ行の先頭に%を続けて書く。
・セミコロン以下の1行は,主釈とみなす。
・ワードおよび定数の間はスペースで区切る。
・負数は2の補数表現。
・文字列は0DHまたは"を終端文字とする。
 

■定数

●10進数(4バイトデータ) <──dH dL>
 先頭に'をつけて表す。取り得る値は'0〜'4294967295。上位,下位の順番でスタックに積まれる。
●10進数(2バイトデータ) <──d>
 取り得る値は0〜65535。
●16進数 <──d>
 先頭に$をつけて表す。取り得る値は$0000〜$FFFF。
●文字列 <──add>
 ダブルクォーテーションで囲まれた文字列の先頭アドレスをスタックに積む。
 

■変数

・A〜Zおよびそれらに1〜9をつけた260個が使える。
・4バイトデータは続くもうひとつの変数が使われる。
例)Aに4バイトデータを代入すると,Aに上位2バイト,A1に下位2バイトが代入される。
 

■出力用ワード

●PRINT <d──>
 スタックトップを,右詰め10進5桁で出力する。
●PRINT1 <d──>
 スタックトップを,左詰め10進で出力する。
●PRINT2 <dH dL──>
 スタックトップの32ビット数を,左詰め10進で出力する。
●PRF <d──>
 スタックトップを,左詰め符号つき10進で出力する。
●PRF2 <dH dL──>
 スタックトップの32ビット数を,左詰め符号つき10進で出力する。
●CHR <c──>
 スタックトップの下位バイトをASCIIコードとみなし,対応する文字を出力する。
●CR <──>
 復帰改行を出力する。
●PRTS <add──>
 スタックトップのアドレスから0DHまたは"の直前までの文字列を出力する。
●COTR <add──>
 スタックトップのアドレスから0DHまたは"の直前までの文字列をコントロール文字列とみなして出力する。
 カーソルコントロール文字列 …… 画面制御
 D …… カーソルを下へ1文字介移動
 U …… カーソルを上へ1文字介移動
 R …… カーソルを右へ1文字介移動
 L …… カーソルを左へ1文字介移動
 C …… 画面をクリア
 / …… 改行する
●HEX2 <c──>
 スタックトップの下位バイトを16進2桁で出力する。
●HEX4 <d──>
 スタックトップを16進4桁で出力する。
●HEXL <dH dL──>
 スタックトップの32ビット数を16進8桁で出力する。
●PRON <──>
 画面出力をプリンタにも出力するようにする。S-OSの#LPTONと周様。
●PROFF <──>
 画面出力をプリンタには出力しないようにする。S-OSの#LPTOFFと同様。
 

■入力用ワード

●KEY <──c>
 キーが押されるのを待って1文字入力し,結果をスタックに積む。
●GETKEY <──c>
 リアルタイムキー入力。キー入力のないときは0を返す。
●FLGET <──c>
 カーソルを点滅させて1文字入力する。
●INP$ <add──>
 キーボードから1行入力し,結果をadd以降に格納する。入力文字列の最後には0DHが付加される。
 

■演算用ワード

●+ <d1 d2──d3>
d1とd2の和を求める(d3)。
●- <d1 d2──d3>
d1とd2の差を求める(d3)。
●* <d1 d2──d3>
d1とd2の積を求める(d3)。
●/ <d1 d2──d3>
d1をd2で割った商を求める(d3)。
●MOD <d1 d2──d3>
d1をd2で割った余りを求める(d3)。
●/MOD <d1 d2──d3 d4>
d1をd2で割った商(d3)と余り(d4)を求める。
●L+ <d0H d0L d1H dlL──d2H d2L>
 スタック上の2つの32ビット数の和を32ビットで求めスタックに積む。
●L- <d0H d0L d1H d1L──d2H d2L>
 スタック上の2つの32ビット数の差を32ビットで求めスタックに積む。
●L* <d0H d0L d1H d1L──d2H d2L>
 スタック上の2つの32ビット数の積を32ビットで求めスタックに積む。
●L/ <d0H d0L d1H d1L──d2H d2L>
 スタック上の2つの32ビット数について2番目をトップで割り,その商を32ビットで求めスタックに積む。
●LMOD <d0H d0L d1H d1L──d2H d2L>
 スタック上の2つの32ビット数について2番目をトップで割り,その余りを32ビットで求めスタックに積む。
●L/MD <d0H d0L d1H d1L──d2H d2L d3H d3L>
 スタック上の2つの32ビット数について2番目をトップで割り,その商と余りをそれぞれ32ビットで求め,余り,商の順にスタックに積む。
●D* <d1 d2──d3H d3L>
 d1とd2の積を32ビット数で求める。
●*! <d1 c──d2>
 スタックトップを8ビット数とみなして積を求める。
●D/MOD <d0H d0L d1──d3 d3H d3L>
 スタックトップの16ビット数で2番目の32ビット数を割り,その商を32ビット,余りを16ビットで求め,余り,商の順にスタックに積む。
●AND <d1 d2──d3>
 d1とd2σ)ANDをとる(d3)。
●OR <d1 d2──d3>
 d1とd2のORをとる(d3)。
●XOR <d1 d2──d3>
 d1とd2のXORをとる(d3)。
●INC# <d1──d2>
 d1に1を加えスタックに積む(d2)。
●DEC# <d1──d2>
 d1から1を引きスタックに積む(d2.)。
●HIGH <d1──d2>
 d1の上位バイトを値としてスタックに積む(d2)。
●LOW <d1──d2>
 d1の下位バイトを値としてスタックに積む(d2)。
●EX <d1──d2>
 d1の上位バイトと下位バイトを交換したものを値としてスタックに積む(d2)。
●NOT <d1──d2>
 d1をビット反転する(d2)。
●NEGATE <d1──d2>
 d1の2の補数をとる(d2)。
●ROR <d1 d2──d3>
 d1の値をd2回右シフトしてスタックに積む(d3)。
●ROL <d1 d2──d3>
 d1の値をd2回左シフトしてスタックに積む(d3)。
●CTL <d1──d2H d2L>
 d1の値を符号つきで32ビット数に変換してスタックに積む。
●== <d1 d2──d3>
 d1とd2の値が等しければ1を,等しくなければ0をスタックに積む。
●!= <d1 d2──d3>
 d1とd2が等しくなければ1を,等しければOをスタックに積む。
●< <d1 d2──d3>
 d1<d2ならば1を,そうでなければ0をスタッ?に積む。
●> <d1 d2──d3>
 d1>d2ならば1を,そうでなければ0をスタックに積む。
●F< <d1 d2──d3>
 符号っき数値として処理する。動作はくと同じ。
●=0 <d1──d2>
 d1が0と等しければ1を,そうでなければ0をスタックに積む。
●CMP2 <d1H d1L d2H d2L──d3>
 2つの32ビット数を比較して,d1>d2ならば1を,d1=d2ならば0を,d1くd2ならば-1(65535)をスタックに積む。
 

■スタック操作用ワード

●DROP <d──>
 スタックトップ(d)を捨てる。
●COPY <d──d d>
 スタックトップ(d)をコピーしスタックに積む。
●SWAP1 <d1 d2──d2 d1>
 スタックトップ(d2)と2番目(d1)を交換する。
●ROT <d1 d2 d3──d2 d3 d1>
 スタックの3番目(d1)がトップにくるように,スタックの上から3個のデータを回転する。
●TR <d──>
 スタックトップのデータ(d)をリターンスタックに積む。
●FR <──d>
 リターンスタックのトップをスタックに積む(リターンスタックのトップは取り去られている)。
●DROPL <dH dL──>
 スタック上の32ビット数を取り去る。
●COPYL <dH dL──dH dL dH dL>
 スタック上の32ビット数をコピーする。
●SWAPD <d0H d0L d1H d1L──d1H d1L d0H d0L>
 スタック上の2つの32ビット数を入れ替える。
 

■機械語リンク用ワード

●CALL <add──>
 addの示すアドレスの機械語サブルーチンをコールする。
●PUTA <c──>
 スタックトップの値をAレジスタにロードする。
●PUTD <d──>
・ス?ックトッフ"O){inをDEレジスヲにロードする。
●PUTH <d──>
 スタックトップの値をHLレジスタにロードする。
●GETA <──c>
 Aレジスタの値をスタックに積む。
●GETD <──d>
 DEレジスタの値をスタックに積む。
●GETH <──d>
 HLレジスタの値をスタックに積む。
 

■メモリ操作用ワード

●PEEKB <add──c>
 addの示すアドレスからの1バイトデータをスタックに積む。
●PEEKW <add──d>
 addの示すアドレスからの2バイトデータをスタックに積む。
●POKEB <c add──>
 addの示すアドレスにcを格納する。
●POKEW <d add──>
 addの示すアドレスに2バイトデータdを格納する。
●STRCPY <add1 add2──>
 add1の示すアドレスから始まる文字列をadd2の示すアドレス以降にコピーする。
●LEFT$ <add1 add2 d──>
 add1の示すアドレスから始まる文字列の左からd文字をadd2の示すアドレス以降に格納する。
●RIGHT$ <add1 add2 d──>
 add1の示すアドレスから始まる文字列の右からd文字をadd2の示すアドレス以降に格納する。
●MID$ <add1 add2 d1 d2──>
 add1の示すアドレスから始まる文字列の左からd1文字目以降のd2文字をadd2に示すアドレス以降に格納する。
●STRCAT <add1 add2──>
 add1の示すアドレスから始まる文字列に,add2の示すアドレスから始まる文字列を連結する。
●STRLEN <add──d>
 addの示すアドレスから始まる文字列の長さをスタックに積む。
●INSTR <add c──d>
 addの示すアドレスから始まる文字列中にcが示す文字があればその位置を返し,みつからなければ0を返す。
●STRCMP <ad1 add2──d>
 add1の示すアドレスから始まる文字列1とadd2の示すアドレスから始まる文字列2を比較し,一致したら0を.文字列1>文字列2なら1を,文字列1<文字列2なら-1を返す。
●STRW <d add──>
 dの値を文字列に変換し,addの示すアドレス以降に格納する。
●STRL <dH dL add──>
32ビット数を文字列に変換し,addの示すアドレス以降に格納する。
●VAL1 <add──d>
 addの示すアドレスから始まる10進文字列を数値化してスタックに積む。
●VAL2 <add──dH dL>
 addの示すアドレスから始まる10進文字列を数値化(32ビット数)してスタックに積む。
●VAL$ <add──d>
 addの示すアドレスから始まる16進文字列を数値化してスタックに積む。
●TRANS+ <add1 add2 d──>
 add1を転送元,add2を転送先として,dバイトをブロック転送する。機械語のLDIRと同等。
●TRANS- <add1 add2 d──>
 add1を転送元,add2を転送先として,dバイトをブロック転送する。機械語のLDDRと同等。
●FILL <add d c──>
 addの示すアドレスからdバイトをcで埋める。
 

■I/O操作用ワード

●IN <add──c>
 addの示すアドレスのI/Oポートから入力された値をスタックに積む。
●OUT <c add──>
 addの示すアドレスのI/Oポートにcを出力する。
 

■特殊ワーク操作用ワード

●PEEK# <add──c>
 addをオフセットアドレスとして,S-OSの特殊ワークエリアの1バイトデータを読み込みスタックに積む。
●POKE# <c add──>
 addをオフセットアドレスとして,S-OSの特殊ワークエリアにcを書き込む。
 

■一般ワード

●WIDCH <c──>
 画面の桁数を指定する。
●BELL <c──>
 cの回数だけビープ音を鳴らす。
●LOCATE <d1 d2──>
 d1をX座標,d2をY座標とする位置へ力ーソルを移動する。
●CURX <──d>
 カーソルのX座標をスタックに積む。
●CURY <──d>
 カーソルのY座標をスタックに積む。
●ASCII <add──d>
 addの示すアドレスから始まる文字列のA5CIIコードをスタックに積む。
●RND <──d>
 0〜65535の範囲で乱数を発生する。
●SCRN <d1 d2──c>
 d1をX座標,d2をY座標とするカーソル位置のキャラクタを読み出す。
●# <──d>
 #変数名
 変数の値を2バイト単位で取り出しスタックに積む。
●## <──dH dL>
 ##変数名
 変数の値を4バイト単位で取り出しスタックに積む。
●. <d──>
. 変数名
 スタックトップの値を変数に代入する。
●.. <dH dL──>
 .. 変数名
 スタックトップの32ビット数を変数に代入する。
●INC <──>
INC 変数名
 変数の値に1を加える。
●DEC <──>
DEC 変数名
 変数の値を1減ずる。
 

■制御用ワード

●GOTO
 GOTO ラベル
 指定行へ分岐する。ワードとラベルのあいだはスペースで区切る。
●GOSUB
 GOSUB ラベル
 サブルーチンを呼び出す。
●RET
 GOSUBに対応するRETURN。
●IF <d──>
 IF words
 スタックトップの値が0以外(要するに真)ならばwordsを実行する。BA51CのIF文と同等。
●REPEAT UNTIL
 REPEAT words UNTIL
 wordsを実行し,UNTILの直前でスタックトップの値が真ならばループを脱し,そうでなければ再び繰り返す。
●DO LOOP!
 d1 d2 DO words LOOP!
 として使用する。d1は始値,d2は終値。wordsを繰り返す。
●;?
 ;?
 ループカウンタの値をスタックに積む。
●J?
 J?
 ひとつ上のループカウンタの値をスタックに積む。
●LEA
 LEA
 ループカウンタの値を終値に一致させループ脱出の条件を満たす。
●BREAK
 BREAKキーが押されているかをチェックし,押されていればプログラムの実行を終了する。
●END
 プログラムの実行を終了する。
 

■グラフィック操作用ワード

●INIT <──>
 画面を初期化する。以下のワードを使用する前に必ずこのワードを実行すること。
●COL <d1 d2──>
 デフォルトのプレーンおよびモードを設定する。d1がプレーン,d2がモードを示す。内容は以下のとおり。
プレーン
0 …… BLUE
1 …… RED
2 …… GREEN
モード
0 ……RESET
1 …… XOR
2 …… OR
3 …… NOP
●PALET@ <cO c1 c2 c3 c4 c5 c6 c7──>
 パレットを設定する(内容はMAGICと同じ)。
●WIND <d1 d2 d3 d4──>
 (d1,d2)-(d3.d4)を結ぶ直線を対角線とする領域をグラフィックウインドウに設定する。
●CLS <c──>
 cで示されるプレーンを消去する。なおcが3のときは全プレーンを消去する。
●DOT <d1 d2──>
 (d1,d2)に点を描画する。
●LlNE@ <d1 d2 d3 d4──>
 (d1,d2)-(d3,d4)問を直線で結ぶ。
●SLlNE@ <d1 d2 d3 d4 d5 d6──>
 (d1,d2)-(d3,d4)-(d5,d6)の3点を滑らかに結ぶ曲線を描く。
●BOX@ <d1 d2 d3 d4──>
 (d1,d2)-(d3,d4)を結ぶ直線を対角線とする長方形を描く。
●TILE <d1 d2──>
 タイルパターンを設定する。
●BOXFUL <d1 d2 d3 d4──>
 (d1,d2)-(d3,d4)を結ぶ直線を対角線とする長方形の領域をタイルパターンで塗りつぶす。
●TRIANGLE <d1 d2 d3 d4 d5 d6──>
 (d1,d2)-(d3,d4)-(d5,d6)を頂点とする3角形の領域をタイルパターンで塗りつぶす。
●CIRCLE <d1 d2 d3──>
 (d1,d2)を中心,d3を半径とする円をタイルパターンで塗りつぶす。
●POINT <d1 d2──d>
 座標(d1,d2)のパレットコードをスタックに積む。
●MAGIC <add──>
 addで示されるアドレスから置かれたデータ列をMAGICに渡す。
 
※文字列関係のワードで転送先を指定するものがありますが,転送先がテキストに重ならないようにしてください。テキストを破壊しかねません。
 また,転送元と転送先のアドレスが重ならないようにしてください。
 
表2 エラーメッセージ
SYNTAX ERROR
STACK EMPTY
RETURN STACK EMPTY
UNDEFINED LABEL
OUT OF LABEL
ワードの記述がおかしい
パラメータスタックが空になった
リターンスタックが空になった
分岐先のラベルが見あたらない
ラベルが2047を超している
 
(C)1990 Shinji Hirai(original)
(C)1997 Junji Okazaki(edited)
(C)2024 Oh!Ishi,Nibbles Lab.(formatted)