構造型コンパイラ言語 SLANG

大貫伸昭
 
 SLANGは,変数は16ビット無符号,1パスてオブジェクトを出力するので非常に高速なコンパイラです。すべての処理は関数の形で記述され,マシン語やデータを直接プログラム中に展開するといったこともできます。FuzzyBASICのように1バイト型と2バイト型の配列を使用でき,メモリやI/O,特殊ワークエリアを配列の形で直接参照するといったことやディスクを使用する場合は分割コンパイル可能,X1の場合最大で30Kほどのオブジェクトを生成することもできます。また,最適化にも結構,凝りましたのでオブジェクトの実行速度もかなり高速です。エラトステネスのふるいの場合100回ループで137秒,配列の初期化にズルをすれば115秒程度までいきます。かなり速いといわれる石上版FuzzyBASICコンパイラが206秒ですからZ80用コンパイラとしては限界に近いのではないでしょうか(ちなみにFM-7のPコンパイラは10回ループで8.7秒,CP/M用のLSICコンパイラは10回ループで7.2秒)。
 ランタイムルーチンは約1Kバイトでオブジェクトの先頭にリロケートして出力されます。またX1turboやMZ-2500ではコメント中はもちろん,関数名や変数にもシフトJISの漢字を使用することができます。
 オブジェクトはBC,DE,HL,IX,IYを保存するサブルーチンの形で出力され,当然のことながらオブジェクトやワークのアドレスは自由に設定可能です。
 長所ばかりそ挙げていても気がひけるので短所をいくつか。
 ポインタを使用することはできません。もっとも間接変数を使用すれば「ポインタもどき」くらいは可能です。また,構造体などの構造化されたデータを扱うことはできません。かろうじて配列は2次元までとれます。
 また,動的な局所変数や局所配列用のワークエリアが1関数あたり240バイトしかありませんので,あまり大きな動的局所変数を使用することができません。動的な局所配列を使用するようにしてください。
 最大の短所はこの言語がCではないtいうことでしょうか。Cライクな部分もありますが,まさに俗語的なオリジナル言語ですので発展性はあまりありません。しかし,これは逆に最大の長所にもなりうるわけです。Cにとらわれていないからこそ,Z80にあった仕様を決定し,機能拡張ができるのです。SLANGの目指すところはHALT76と同じものでした。
 

◆使用方法

 配布されたプログラムはディスク対応版となっています。テープやQDを使用する場合は3006Hの内容を00Hに書き換えてください。なお,オンメモリ版では#INCLUDEによるソース取り込みなどはできません。
 SLANGは専用コンパイラですから,ソースプログラムはE-MATEなどのエディタ上で作成する必要があります。コンパイラはディスクやテープ上のASCIIファイル,メモリ上のソースプログラムをコンパイルします。
 起動の際は,コールドスタートアドレス(3000H)をコールし,まずコマンドモードに入ります。ユーザーはこのモードから表1のようなコマンドを使ってSLANGを操作するわけです。
 

◆SLANG言語仕様

 配布ディスクには,SLANGを利用したアプリケーションがいくつか収録されていますので,それらのソースを見ながらSLANGを学んでいってください。簡単に説明しますと,SLANGは基本的にフリーフォーマットですからBASICのように1行に処理を収めなくてもよいのです。コンパイラですから各自の読みやすいような形式で記述してください。
 たいていのSLANG用プログラムのメイン部分を見て気がつくのは,各処理が関数として扱われているこtでしょう。たとえば,BASICではステートメントであったFOR文などの制御構造もわりと関数っぽく記述されています(}のあとにNEXTが付くなど崩れていますが)。
 基本的にSLANGの関数は,
  関数名(パラメータなど) 処理内容
という書式で表されます。処理内容として記述できるのは1文のみですが,複合文(文括弧でくくられている処理は1文と見なされる)を使用することも可能です。リスト中ては文指弧にS-OS標準のキャラクタセットにはない{,}が使用されている部分もあります。文括弧としてはかに(,),「,」,[,],BEGIN,END;の各対応が用意されています。お好みにあわせて使用してください。
 次に自がつくのは条件付きコンパイルの部分を行っている#IFなどのコマンドでしょう。SLANGではコンパイル中にコンパイラにコマンドを与えることによって,このような条件付きコンパイルを行うことが可能です。
 そのほかの書式などはPASCALやCなどのアルゴル系言語に似たものとなっています。プログラム中の=は基本的に値の定義,代入の意味で使用され,論理式での「等しい」という意味には==が使用されます。また,COUNT++はCOUNT=COUNT+1と同義,EFはELSE IFと,FIはEND IF(ようするにIFの反対)と同義となっています。詳しくは,リファレンスとサンプルプログラムを参照してください。
 文法については(あくまでも俗語ですのであまり参考にはならないかもしれませんが)Cなどの文法書を見てみるのもよいかもしれません。
 そして,プログラムの3006Hから3015Hまでの部分は各種のスイッチとして使用されています。詳しい内容は表2のとおりですが,起動する前にこれらの部分を書き換えておくことにより,作成するプログラムにあわせてワークエリアやメモリの使用状況を自分の思いのままにすることが可能です(プログラム中からも指定できます)。
 
表1 SLANGモニタコマンド
C filename (ソースファイルをコンパイルする)
 ファイル名を省略した場合メモリ上のソースがコンパイルされる。また,C/とすることでリスト付きコンパイルとなる
X nn (ソースを格納するアドレスをnnに変更)
 メモリ上にあるソースをコンパイルする場合はこのコマンドでアドレスを指定する。
S nn1 nn2 nn3 nn4:filename (オブジェクトをセーブする)
 nn1~nn4はそれぞれ先頭・最終・実行・格納アドレスを表す。オフセットをつけない場合はnn4を省略する。
D デバイス名 (ディレクトリを表示する)
DV デバイス名 (デフォルトデバイスを変更する)
# (プリンタのON/OFFを切り替える)
JまたはGnn (アドレスnnをコールする)
! (S-OSのモニタにジャンプする)
M (各機種のモニタにジャンプする)
 
図 メモリマップ
0000H
 
 3000H
 
 
7400H
 
8000H
 
 
 
B000H
 
#MEMAX
 
S-OS
 
SLANG
 
クラスタBUFF
 
 
 
 
オブジェクト
コード
 
 
 
 
 
*1
 
 
 
 
 
*2
 
 
  DISK版  
7400H
 
 
 
B000H
 
#MEMAX
ソース
プログラム
 
 
オブジェクト
コード
*3
 
 
 
 
*2
 
  ONメモリ版  
 
0000H
 
 
 0200H
 
 
 
0300H
 

局所表
 

ハッシュ表
 
大域表
 
 
*4
 
 
 
*5
 
 
*6
 
 特殊ワークエリア 
●コンパイル時
*1 クラスタBUFFの初期値
*2 OBJ初期値の初期値。DISK版の場合8000Hにするとよい
*3 TEXT TOPの初期値
*4 局所表TOPの初期値
*5 ハッシュ表TOPの初期値
*6 大域表TOPの初期値
 
0000H
 
 
 
 3000H
 
 
 
 
 
8000H
 
 
 
 
 
 
 
 
 
 

S-OS
 
 
 
 
 
 
 
RUN TIME
 
オブジェクト
&静的ワーク
動的ワーク

 
 
0000H
 
 
 
 3000H
 
 
6000H
 
 
8000H
 
 
 
 
 
  
 
 
 
  

S-OS
 
 
 
 
静的ワーク
 
動的ワーク

 
RUN TIME
 
オブジェクト
&静的ワーク
の一部
ORG宣言・WORK宣言を省略した場合
ORG宣言を省略し,WORK $6000とした場合
●実行時 (スタックはコールされた時点のスタックを使う)
 
表2 初期値およびスイッチ
3006H : DISK 初期値…1
0 : ONメモリ版
1 : ディスク版
3007H : セミコロンチェック 初期値…1
0 : チェックしない
1 : チェックする
3008H : OBJ初期値(下位) 初期値…$B000
3009H : OBJ初期値(上位)
オブジェクトコードを生成する先頭アドレス。ORG宣言を省略した場合使用される。ディスク版の場合8000Hにするとよい
300AH : ランタイム最終ADR(下位)
300BH :    〃   (上位)
ランタイムルーチンの最終アドレス。ランタイムルーチンを追加する場合変更する
300CH : クラスタBUFF(下位) 初期値…$7400
300DH :    〃   (上位)
ディスク版で,ソースを読み込むための4Kバイトのワークの先頭アドレス
300EH : TEXT TOP(下位) 初期値…$7400
300FH : TEXT TOP(上位)
ONメモリ版で,ソースを格納する先頭アドレスの初期値。 Xコマンドで変更することができる
3010H : 局所表TOP(下位) 初期値…$0000
3011H :    〃   (上位)
S-OS特殊ワークエリア上に取られる局所表の先頭アドレス。通常は変更しない
3012H : ハッシュ表TOP(下位) 初期値…$0200
3013H :    〃   (上位)
S-OS特殊ワーク上に取られる大域表のためのハッシュ表のアドレス。同時に局所表の上限でもある
3014H : 大域表TOP(下位) 初期値…$0300
3015H :    〃   (上位)
S-OS特殊ワーク上に取られる大域表の先頭アドレス。同時にハッシュ表の上限でもある
 

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

■書式に関する規定

●フリーフォーマット
 基本的にはC言語のようにフリーフォーマットで,行の概念はなく,名前などの途中以外ではどこで区切ってもよいがいくつか例外がある。
・//コメント //以降はコメントとみなされ,その行の終わリまで無視される
・“文字列” 2行にまたがることはできない
・配列 配列名と[の間を空白などで区切ることはできない
・関数 関数名と(の間を空白などで区切ることはできない
●空白
 空白は名前などの途中と配列の[および関数の(の前以外ならどこに置いてもよい 。以下のものは空白と同等である。
  改行
  コメント
  #コマンド
●コメント
 注釈文。空白が置けるところなら,どこに置いてもよい。
  //コメント //から行の終わりまで
  /*コメント*/ /*から */まで。ネスティング不可
  (*コメント*)  (*から*)まで。ネスティング不可
●#コマンド
 コンパイラに対する命令。空白が置けるところなら,どこに置いてもよい。
  #INCLUDE ファイルネーム
 別のソースをその場所に取り込む。ネスティング不可。ファイルネーム以降は行の終わりまで無視される。オンメモリ版では使用できない。
  #CHAIN ファイルネーム
 続きのソースを読み込む。ファイルネーム以降はすべて無視される。オンメモリ版の場合,準備がよいかどうか聞いてくるので,なにかキーを押すと読み込みを始める。BREAKキーを仰すとコンパイルを中止する。
  #IF 式
  #ELSE
  #ENDIF
 条件付きコンパイルを行う。#IFの後ろの式が真ならば#ELSEまでを,偽ならば#ELSEから#ENDIFまでをコンパイル。
●アドレス宣言
 オブジェクトコードやワークエリアの先頭アドレスを指定する。宣言を省略することもできる。
  ORG宣言
 オブジェクトコードの先頭アドレスを指定する。宣言を省略した場合はデフォルト値が使われる。実際には,先頭にランタイムルーチンがリロケートされ,その後ろにオブジェクトコードが続く。
  WORK宣言
 変数や配列のワークエリアの先頭アドレスを指定する。まず静的なワークエリアが取られ,その後ろから$FFFFに向かって,動的なワークエリアが伸びていく。宣言を省略した場合は,静的なワークエリアはオブジェクトコード中に埋め込まれ,動的なワークエリアはオブジェクトコードの後ろから$FFFFに向かって伸びていく。ただし,初期値を持つ静的なワークエリアの場合は,宣言の有無にかかわらず,オブジェクトコード中に埋め込まれる。
  OFFSET宣言
 コードを生成する際のオフセットを指定する。ZEDAやFuzzyBASICコンパイラのOFFSETと同じ。宣言を省略した場合はオフセットは0となる。
●プログラム
 アドレス宣言,大域宣言とブロック(関数定義)からなる。必ず,
   MAIN( )
という関数が必要で,プログラムを実行させることは関数MAIN( )を実行させることである。関数MAIN( )の定義はプログラムのどこにあってもよい。
●ブロック
 関数頭書き,局所的宣言(静的宣言と局所宣言)と関数定義からなる。
  SUB (X,Y)
  VAR I;
  BEGIN
   I=X+Y;
   RETURN (I);
  END;
(* 関数頭書き *)
(* 静的宣言 *)
(* 関数定義 *)
 局所的なまとまりで,この中で宣言された名前はこの中でのみ有効となる。
●名前の有効範囲
  局所的な名前
 静的宣言や局所宣言で宣言された名前や仮引数,ラベル名は局所的な名前となり,その関数内でのみ使用できる。大域的な名前に向じ名前があった場合,局所的な名前を優先する
  大域的な名前
 関数名や大域宣言で宣言された名前は大域的な名前となり,プログラム全体で使用できる。関数名以外は,宣言された以後有効となる
●大域宣言
 大域的な名前を宣言する。変数や配列は静的にメモリに割り付ける。
●静的宣言
 局所的な名前を宣言する。変数や配列は静的にメモリに割り付ける。
●局所宣言
 局所的な名前を宣言する。変数や配列は動的に取る。ただし,そのワークエリアの合計は1関数240バイト以内でなければならない。
●型
  BYTE, ! … 1バイト型
  WORD, % … 2バイト型
 

■データ形式

●変数
 符号なし16ビット長整数を扱い,型はない。単純変数と間接変数があり,必ずVAR宣言か仮引数で宣言してから使用する。間接変数は,変数としても,配列としても扱える。FuzzyBASICの変数.メモリ配列の扱いと同じ。間接変数自体に型はないが,配列として使用するため1バイト型か2バイト型かを宣言しなければならない。省略した場合は,2バイト型とみなされる。たとえば,POINTが2バイト型の間接変数として宣言されていたとすると,
  POINT=$C000;
  I=POINT[3];
では,$C006の内容を下位バイト,$C007の内容を上位バイトとして,変数Iに代入される。
 単純変数は変数としてのみ使用でき,配列としては扱えない。
●VAR宣言
 変数を宣言する。仮引数リストも書式は同じ。複数の変数を宣言する場合には","(カンマ)でつなぐ。
 単純変数は
  VAR HENSUU, ABC;
のように変数名を書けばよい。間接変数は配列として使用する際の型を宣言し,変数名に[ ]を付けた形で宣言する。
  VAR BYTE POINT[ ], %KANSETU[ ];
以上のように型を省略した場合は2バイト型とみなされる。後ろに:定数式とすることにより,変数の格納アドレスを指定することができる。
  VAR XY:$C000, BYTE Z[ ]: $D000;
と書くと,$C000と$C001を変数XYの格納アドレス,$D000と$D001を変数Zの格納アドレスとすることができる。また,=定数式とすることにより,変数の初期化をすることができる。この場合,WORK宣言がなされていても,その変数のワークはプログラム中に埋め込まれる。
  VAR A=0, B=3, C[ ]=$C000;
 この初期化はコンパイル時にのみ行われ.実行時には行われない。ただし,変数の格納アドレス指定と初期化は大域宣言と静的宣言のみ使用でき,局所宣言や仮引数リストでは使用できない。
 

■定数

 基本的には,16ビット長の符号なし整数で0から65535までの値を取るが,2の補数表現の符号付きの整数と見ることができる。
●10進数
 数字からなる文字列。
  例) 1234, -5
●16進数
 $で始まり16進数からなる文字列か数字で始まり,かつ16進数からなり最後にHが付く文字列。
  例) $ABCD, 12ABH, 0FFFFH
●2進数
 0と1からなる文字列で,最後にBが付く。
  例) 111011001010B
●文字定数
 '(シングルクォーテーション)でくくった1文字で,文字のASCIIコードを値とする。エスケープ文字が使用可。
  例) 'A', '\N', '\'
●文字列定数
 "(ダブルクォーテーション)でくくった文字列で,文字列が格納されているアドレスを値とする文字列は,オブジェクトコード中に埋め込まれ,自動的に最後に$00が付けられる。エスケープ文字が使用可。ただし,2行にまたがることはできず,定数式にも使用できない。
  例)"メッセージ¥n"
●記号定数
 CONST宣言で定義された値を持つ。
●$
 次に生成するオブジェクトコードのアドレスを値とする。
●エスケープ文字
 文字定数や文字列定数中に使われ,2文字で1文字として扱われる。\の後ろに1文字を付けた形で使用されるが,該当する文字がない場合は\だけで1文字となる。大文字と小文字の区別はしない。
(編注:この項のみ,バックスラッシュを全角文字で表記している。ブラウザや環境によっては半角¥記号がバックスラッシュとして表示されるが,誌面掲載時としてはその方が正しい表記となる。実際の使用時は当然ながら半角文字で表記すること)
  \\ … \
  \" … "
  \' … '
  \N … $0D
  \/ … $0D
  \C … $0C
  \R … $1C
  \L … $1D
  \U … $1E
  \D … $1F
  \0 … $00 (MZ以外では\は¥)
●CONST宣言
 記号定数を定義する。複数の記号定数を定義する場合は"," (カンマ)でつなぐ。
  CONST PC=$8001, MZ=2000;
とすると,以後PCは $8001,MZは2000という定数値を持つ。CONST宣言は静的宣言と局所宣言の差異はなく,どちらも局所的な記号定数の宣言となる。
 

■配列

 1バイト型と2バイト型がある。単純配列と間接配列とシステム配列があり,単純配列はARRAY宣言で,間接配列はVAR宣言か仮引数で間接変数として宣言してから使用する。システム配列は宣言しない。1バイト型は1バイト単位で,2バイト型は2バイト単位で,配列要素をアクセスする。アクセス時に添字のチェックはしない。システム配列はメモリやI/O,S-OS特殊ワークエリアを配列の形で直接アクセスする。
 単純配列はARRAY宣言で宣言する。たとえば,
  ARRAY BYTE BUFF[10];
と宣言すると, 1バイト数の配列がBUFF[0]からBUFF[10] ま で の11個確保される。単純配列名は配列のワークの先頭のアドレスを指す定数として扱われるが,動的な(局所宣言で宣言された)配列名は定数式には使用できない。間接配列は間接変数を配列として使用する。 間接変数の値をインデックスとしてメモリをアクセスする。FuzzyBASICのメモリ配列と同じ。型は間接変数を宣言する際に指定する。省略した場合は2バイト型と見なされる。また,間接配列名は変数である。
●ARRAY宣言
 単純配列を宣言する。複数の配列を宣言する場合は","(カンマ)でつなぐ。
  ARRAY BYTE ABUF[5], WORD C[3];
のように型配列名[定数式]の形で宣言すると,定数式+1個分の配列が確保される。添字を省略すると0とみなされ,1個分の配列が確保される。型を省略すると,2バイト型とみなされる。
 二次元までの配列変数を宣言可能。
  ARRAY BYTE ABC[5][3];
 後ろに:定数式とすると,配列の格納アドレスを指定することができる。
  ARRAY ABC[10]: $C000;
とすると,$C000以降を配列ABCのワークエリアとし,ABC[0]の格納アドレスは$C000と$C001,ABC[1]は$C002と$C003,ABC[2]は$C004と$C005,…となる(配列ABCは2バイト型のため)。この場合,添字は意味を持たないので,
  ARRAY ABC[ ]: $C000;
としてもよい。
 また={CODEリスト}とすると,配列を初期化することができる。ただし,{ }は文括狐。この場合,WORK宣言がなされていても,その配列のワークエリアは.プログラム中に埋め込まれる。
  ARRAY BYTE DT[4]={0, 1, 2, 3, 4};
 初期値が足りない場合は,残りは0で埋められる。多すぎる場合は,エラーとなる。添字が省略された場合はチェックしない。ただし,配列の格納アドレス指定と.初期化は,大域宣言と静的宣言のみで使用でき,局所宣言では使用できない。
 

■関数

●関数頭書き
 定義する関数名を宣言する。ブロックの最初に書き,以後,静的宣言や局所宣言,関数定義が続く。
  関数名(仮引数リスト)
の型で書く。仮引数リストの書式は,局所宣言のVAR宣言の書式と同じ。仮引数を持たない場合は,
  関数名( )
と書く。
 仮引数は関数コール時の実引数の値を持ち(値渡し),自動的に動的な局所変数として宣言される。MACHINE宣言されたマシン語関数は,
  関数名(引数の数)
と書く。ただし,引数の数が0個の場合と,引数の数を省略して宣言する場合は,
  関数名( )
と書く。
●関数定義
 関数を定義する。
  BEGIN
    局所宣言;
    文;
    …
    文;
  END;
の形で書く。
  END(式);
とすると,式の値を関数の値として返すことができる。
●関数
 ユーザー関数とシステム関数とMACHINE関数がある。
 ユーザー関数はプログラム中で定義した関数。引数を渡すのにIYレジスタをポインタとして使用する。
 システム関数には,CODE関数とPRINT関数がある。
 MACHINE関数はMACHINE宣言した関数で,ユーザー関数と異なり,レジスタやスタックを使って引数を渡す。主に,外部のマシン語サブルーチンをMACHINE関数として宣言するが,プログラム中でCODE関数を使って定義したマシン話関数もMACHINE関数とすることができる。
●関数コール
 値渡しである。
  関数名(実引数リスト)
の形で関数を呼び出す。実引数と仮引数の数が合わないと,エラーになる。
  RETURN (式);
  END (式);
によって返される値が関数の値となる。MACHINE関数で引数の数を省略して宣言した場合のみ,引数の数のチェックを行わない。
●MACHINE宣言
 マシン語関数を宣言する。複数のマシン語関数を宣言する場合は"," (カンマ)でつなぐ。
  関数名(引数の数)
の形で宣言する。ただし,その関数を使用,定義する前に宣言しなければならず,また,大域宣言でのみ宣言できるので,通常,アドレス宣言の次の大域宣言で宣言する 。
  MACHINE MSUB(2): $C000;
のように後ろに:定数式を付けると,外部にあるマシン語サブルーチンを関数として利用できる。上の場合MSUBは$C000にあり,引数を2個持つ関数となる。
 引数の数が0個の場合は,マシン語関数ではなく,引数を持たないふつうの関数として扱われるので, プログラム内の関数の宣言は無意味である。外部の関数の宣言に使う。
  MACHINE MON(0): $1F8E;
 引数の数が1個から3個までの場合は,レジスタを使って引数を渡す。引数の数が4個以上の場合は,スタックを使って引数を渡す。引数の数を省略した場合は,スタックを使って引数を渡し,HLレジスタに引数の数が代入される。この場合に限り,引数の数のチェックは行わない。
  MACHINE PRINTF( );
●関数コールの実際
 引数や,動的な変数や配列のポインタとしてIYレジスタを使用している。
 実引数の場合,
  SUB(A, B)
とすると,
  LD HL, (VARA)
  LD (IY+$70), L
  LD (IY+$71), H
  LD HL, (VARB)
  LD (IY+$72), L
  LD (IY+$73), H
  CALL SUB
というコードが生成される。一方,関数側では,たとえば,
  SUB(I, J)   (* 仮引数 *)
   VAR K;   (* 静的宣言 *)
   BEGIN
     VAR L; (*局所宣言*)
     〜
   END;
となっていたとすると,動的な変数は3個(I, J, L)となり,関数の最初と最後に,
  PUSH IY
  LD BC, 6
  ADD IY, BC ; 3個×2バイト
    〜
  POP IY
というコードが生成される。
 ただし,動的な変数や配列がない場合は,なにも生成されない。
 この関数内での動的な変数のアドレスは次のようになる。
I ……

J ……

L ……
(IY+$6A) 下位
(IY+$6B) 上位
(IY+$6C) 下位
(IY+$6D) 上位
(IY+$6E) 下位
(IY+$6F) 上位
 つまり,Iの初期値は実引数Aの値,Jの初期値は実引数Bの値,Lの初期値は不定となる。
 実引数用のワークは(IY+$70)から(IY+$7F)までであるため,引数の数は最大8佃までとなる。実引数に関数を使用する場合,引数が8個以下でもワークがあふれてしまいエラーになる場合があるので注意すること 。
 動的な変数や配列のワークは(IY+$80)から(IY+$6F)までの240バイトしかないので. 大きな動的配列を宣言する場合は,注意すること。関数の値は,関数から戻ってきたときのHLレジスタの値となる。MACHINE関数の場合は,宣言した引数の数によって呼び出し方が異なる。
0個  
1個  
2個  
3個  
4個以上
省略  
……CALLのみ
……HLレジスタに引数を代入してCALL
……順にHL,DEに代入してCALL
……順にHL,DE,BCに代入してCALL
……スタックに積んでCALL
……スタックに積み,HLに引数の数を代入してCALL
例) スタックに積んだ様子 SUB(A,B,C);
アドレス大





SP
アドレス小
A
B
C
リターンアドレス
 MACHINE関数で,動的な変数や配列を使用する場合も,ユーザー関数と同様のIYレジスタの退避が行われるので注意すること。
 関数の値は,関数から戻ってきたときのHLレジスタの値となる。
 

■演算子

●式
 式はすべて16ビット長で演算を行う。
 真は1,偽は0。
●ビット演算子
AND
OR
XOR
CPL
<<
>>
HIGH
LOW
論理積
論理和
排他的論理和
ビット反転
左シフト
右シフト
上位8ビットを値とする
下位8ビットを値とする
●論理演算子 (真のとき1,偽のとき0を値とする)
NOT
論理否定
●関係演算子 (真のとき1,偽のとき0を値とする)
==
<>
!=
>
>=
<
<=
等しい
等しくない

大きい
大きいか等しい
小さい
小さいか等しい
●代入演算子
=
代入
●カンマ演算子
,
左から右へ計算され,最も右の論理項を値とする
●算術演算子
  + (単項)
  - (単項)
  +
  -
  *
  /
  MOD
正符号
負符号
加算
減算
乗算
除算
剰余算
●ピリオド演算子 (符号付きで演算を行う)
  .*. ./. .MOD. .<<. .>>. .<=. .>=. .<. .>.
●その他
++
インクリメント演算子
変数や配列の値に1を加える。変数や配列の前に置いた場合は,+1してから値が参照され,後ろに置いた場合は,値が参照されてから+1する
--
デクリメント演算子
変数や配列の値から1を引く。前置き,後ろ置きの規則は++と同じ
&
アドレス演算子
変数や配列が格納されているアドレスを値とする。ただし,システム配列には使用できない
?:
C言語の条件演算子と同じ。三項演算子
●演算の優先順位
  1. ( ) [ ]
  2. ++ -- &
  3. + - HIGH LOW NOT CPL (すべて単項演算子)
  4. * / MOD << >> .*. ./. .MOD. .<<. .>>.
  5. + -
  6. == <> != <= >= <> .<=. .>=. .<. .>.
  7. AND OR XOR
  8. ? : (三項演算子)
  9. =
  10. , (カンマ)
 

■システム構成

●文
 { }は文括弧を表す。文括弧として[ ],( ),「」,BEGIN END; が使用できる。[ ]は省略可を表す。
●ラベル
 ラベル名:
 GOTO文やEXIT TO文のジャンプ先を指定する。ラベル名は局所的な名前となる。
●式文
 式;
 式の文。
●復合文
 {文 [, 文, …, 文] }
 複数の文を文括弧でくくり,ひとつの文として扱う。
●空文
 ;
 なにもしない文。
●IF文
 IF 文 {THEN} 文1 [ELSE 文2] [ENDIF;]
 式の値が真ならば文1,偽ならば文2を実行する。
 文2がIF文の場合,ELSE IFをELSEIFまたはEFと書くことができる。
●FOR文
 FOR 単純変数名= 式1 TO   式2 [DO] 文 [NEXT;]
  〃   〃    〃 DOWNTO 〃  〃 〃   〃
 単純変数の値を式lから式2になるまで1ずつ増やし,文を繰り返す。DOWNTOの場合は1ずつ減らす。
 まず文を実行してから,終値の判定を行う 。 ただし,式1と式2が間に0をはさむ場合は,期待される繰り返しは行われず,1回で繰り返しを終了する。
●WHILE文
 WHILE 式 [DO] 文 [WEND;]
 式の値が真のあいだ,文を繰り返す。
●REPEAT文
 REPEAT 文 UNTIL 式 ;
 式の値が真になるまで,文を繰り返す。
●LOOP文
 LOOP 文 ;
 永久ループ。WHILE文の式の値が常に真/REPEAT文の式の値が常に偽であるのと同等。
●EXIT文
 EXIT;
 FOR文,WHILE文,REPEAT文から脱出する。C言語のbreak文と同じ。
 EXIT TO ラベル名;
 ラベルにジャンプする。ただし,あと戻りはできない。
 EXIT(式);(※)
 式の値の数だけループを抜ける。
●RETURN文
 RETURN;
 その関数を終了して,呼び出した関数に戻る。
 RETURN(式);
 式をその関数の値として,呼び出した関数に戻る。
●GOTO文
 GOTO ラベル名;
 ラベルにジャンプする。EXIT TO ラベル名;と違って,ジャンプ先に制限はない。
●CASE文
 CASE 式0 [OF] {
   定数式1 [:] 文1
   [定数式2 [:] 文2]
   … … …
   [OTHERS [:] 文]
 }
 式0の値が定数nと等しければ,文nを実行し,CASE文を脱出する。上から順に比較していき,いずれの定数式とも等しくなかった場合は,OTHERSの後ろの文を実行する。
  定数式1 TO 定数式2 [:] 文1
とすると,式0の値が定数式1以上,定数式2以下の場合,文1を実行する。
  定数式1, [定数式2, …,] 定数式n [:] 文1
とすると,式0の値が定数式1から定数式nまでのいずれかに等しい場合,文1を実行する。
●登録済みの名前
 システム関数やシステム配列など,登録済みの名前は,すべて大域的な名前である。
●登録済みの記号定数
  FALSE 値は0
  TRUE 値は1
 以上2つがシステムに登録済みの記号定数である。
 

■システム配列

●MEM[式]
 式の値のアドレスの内容を1バイト単位でアクセスする。
●MEMW[式]
 式の値のアドレスの内容を2バイト単位でアクセスする。式のアドレスが下位バイト,式+1のアドレスが上位バイトに対応する。
●PORT[式]
 式の値のI/Oポートを1バイト単位でアクセスする。
●PORTW[式]
 式の値のI/Oポートを2バイト単位でアクセスする。式のI/Oポートが下位バイト,式+1のI/Oポートが上位バイトに対応する。下位バイト,上位バイトの順にアクセスされる。
●SOS[式]
 式の値のS-OS特殊ワークエリアを1バイト単位でアクセスする。
●SOSW[式]
 式の値のS-OS特殊ワークエリアを2バイト単位でアクセスする。式の特殊ワークエリアが下位バイト,式+1の特殊ワークエリアが上位バイトに対応する。
 

■登録済みの変数

●^A
 CALL関数. GETREG関数で使用。CALL関数では値をAレジスタに代入してからマシン語ルーチンをコールし,終了後Aレジスタの値が代入される。^は↑でも可。
●^BC
 ^DE
 ^HL
 ^IX
 ^IY
 ^Aと同様。
●^AF
 ^Aと同様。^AFの上位バイトと^Aの下位バイトは同じ値を持つ。
●^SP
 CALL関数 .GETREG関数で使用。現在のSPの値が代入される。
●^CARRY
 ^CY
 CALL関数,GETREG関数で使用。CYフラグが立っていれば1,立っていなければ0が代入される。
●^ZERO
 Zフラグ。^CARRYと同様。
●@KBUFF
 キー入力用パッファのアドレスを値として持つ。代入すると,S-OSの#KBFADの値が変わってしまうので注意すること。
 

■登録済みの基本関数

●BEEP( )
 BEEP音を鳴らす。S-OSの#BELL。
●STOP( )
 プログラムの実行を終了する。
●LOCATE(X座標, Y座標)
 カーソルを移動する。
●INKEY(n)
 入力されたキーの値を返す。
   n=0のときS-OSの#GETKYと同じ
   n=1のときS-OSの#FLGETと同じ
   その他のときS-OSの#INKEYと同じ
●INPUT( )
 キーボードから入力された数値を返す。先頭に$を付けると,16進数とみなす。コールした時点のカーソル以降を読み込み,正常な入力が行われた場合は^CARRY=0,BREAKキーが押されたり誤入力があった場合は^CARRY=1となる。
●GETL(格納アドレス)
 キーボードから1行入力し,格納アドレスに格納し,行の長さを返す。BREAKキーが押された場合は-1を返す。行の最後は0となる。
●GETLlN (格納アドレス, 長さ)
 1行の最大長を指定できるほかは,GETL関数と同じ。オーバーした分は無視される。
●LlNPUT(格納アドレス, 長さ)
 コールし た時点のカーソル以降を続み込むほかはGETLlN関数と同じ。
●WIDTH(n)
 画面モード(40キャラ,80キャラ)を切り替える。nが40以下だと,40キャラ,40より大きいと80キャラとなる。S-OSの#WIDCH。
●SCREEN(X座標, Y座標)
 画面のキャラクタを読み出し,キャラクタコードを返す。S-OSの#SCRN。
●PRMODE(n)
 PRINT関数の出力を切り替える。
   n=0のとき,画面にのみ出力
   n=1のとき,画面とプリンタに出力
   その他のとき,プリンタのみに出力
●BIT(値, n)
 値の第nビットを調べ,0か1かを返す。nの値は0から15まで。
●SET(値, n)
 値の第nビットを1にする。
●RESET(値, n)
 値の第nビットを0にする。
●ABS(n)
 nを2の補数表現の符号付きの値とみなし,その絶対値を返す。
●SEX(n)
 nを符号付き1バイトの値とみなし,符号付き2バイトの値にして返す。
●SGN(n)
 nを符号付きの値とみなし,正なら1,0なら0,負なら-1を返す。
●RND(n)
 0からn-1までの乱数を返す。
●VTOS(値, BUFF)
 値を10進数の文字列に直してBUFFに格納する。文字列の最後は$00になる。BUFFは6バイト必要。
●GETREG( )
 各レジスタなどの値,それぞれ変数,^AF,^BC,^DE,^HL,^IX,^IY,^CARRY,^ZERO,^SPに代入する。単独で用いること。
●CALL(アドレス)
 各レジスタに,変数^A,^BC,^DE,^HL,^IX,^IYの値を代入して,アドレスをコールする。コールが終了すると,GETREG( )と同様の処理をし,HLレジスタの値を返す。
 

■システム関数

●CODE関数
 直接データをオブジェクトに落とすための関数。式中で使われる場合は,マシン語データを実行後HLレジスタを返り値とする。
●CODEリスト
 CODE関数や配列の初期化など,データを直接オブジェクトに落とすための書式。CODE項を","(カンマ)でつなぐ。
●CODE項
・"文字列"
 文字列をそのまま,オブジェクトに落とす。文字列定数のように,自動的に最後に$00を付けることはしない
・[式]
 式の値をHLレジスタに代入するようなオブジェクトを作る。その他のレジスタの値は保証されない
・<ラベル名>
 ラベルのアドレスを,下位バイト,上位バイトの順で2バイトのオブジェクトにする
・型, 定数式
 1バイト型なら,定数式の値の下位バイトを1バイトのオブジェクトにし,2バイト型なら,下位バイト,上位バイトの順で2バイトのオブジェクトにする。型を省略した場合は1バイト型とみなされる
●PRINT関数
 文字や数値を画面やプリンタに出力する。PRMODE関数で出力先を変えることができる。
●書式リスト
 PRINT関数の書式。書式項を","(カンマ)でつなぐ。
●書式項
・"文字列"
 文字列をそのまま出力。
・/(スラッシュ)
 改行する。
・値
 値を10進左詰め出力。
・FORM$(値, n)
 値を10進n桁右詰め出力。
・DECI$(値)
 値を10進5桁右詰め出力。
・%(値)
・PN$(値)
 値を符号付き10進左詰め出力。
・HEX2$(値)
 値を16進2桁出力。
・HEX4$(値)
 値を16進4桁出力。
・MSG$(値)
 値のアドレスから$0Dの直前までをASCII出力。
・MSX$(値)
 値のアドレスから$00の直前までをASCII出力。
・!(値)
・STR$(値, n)
 値のキャラクタをn個出力。
・CHR$(n)
 値を上位バイト,下位バイトの順にASCII出力。
・SPC$(n)
 空白をn個出力。
・CR$(n)
 改行をn個出力。
・TAB$(n)
 力ーソルをn回右へ移動。
 
■エラーメッセージ
Missing "文字" あるべき文字がない
Syntax error 文法エラー
Illegal constant 正しい定数式ではない
Illegal brace 文括弧エラー。あるべき文括弧がない。また開きと閉じの括弧が合わない
Bad string 文字列エラー。$20以下のコードがある
Illegal name 名前を誤使用している
Dup def name 二重に宣言している
Undef array 未宣言配列
Undef var 未宣言変数
Illegal address アドレス宣言のアドレス指定が正しくない
Too many arguments 引数が多すぎる。引数のワークは8個分しかない
Too many data データが多すぎる
Out of range 値が大きすぎる
Local area overflow 局所域がいっぱいになった。動的局所域は240バイトしかない
Unmatched arguments 引数の数が合わない
Dev by 0 0で割っている
Missing UNTIL UNTILがない
Missing TO/DOWNTO TO/DOWNTOがない
Can't jump ジャンプできない
Nesting overflow ループの入れ子が深すぎる。16レベルまで
Global table overflow 大域表がいっぱいになった
Local table overflow 局所表がいっぱいになった
Too long line 1行が長すぎる。1行は255文字以内に収める
Too long name 名前が長すぎる。名前は32文字以内に収める
Can't include INCLUDEできない。オンメモリ版ではINCLUDEできない。入れ子は8レベルまで
Undef func 未宣言関数
Undef label 未宣言ラベル
Memory over メモリがオーバーした
 
(C)1988 Nobuaki Onuki(original)
(C)1997 Junji Okazaki(edited)
(C)2024 Oh!Ishi,Nibbles Lab.(formatted)