TL/1 はPacal likeな 8 bit cpu 用の 1 pass コンパイラです。1980年に大西博氏によってMC6800用に設計実装されました。 高速なコンパイルが特徴ですが変数と配列は8bitのみ。16bit アドレスのメモリ空間にはMEM変数を使ってアクセスします。
% TEST PROGRAM ** PROC WAIT,TIME %--- MAIN --- VAR I BEGIN WRITE(1:"Do ") FOR I:=1 TO 10 DO [ WRITE(1:I,CRLF) TIME ] WAIT END %-- PROCEDURE WAIT -- WAIT VAR I,J,K BEGIN FOR I:=0 TO 1 DO [ FOR J:=0 TO 255 DO [ FOR K:=0 TO 255 DO []]] END %-- PROCEDURE TIME -- TIME VAR I,J BEGIN FOR I:=0 TO 10 DO [ FOR J:=0 TO 150 DO []] ENDt2.tl1 t3.tl1
この文章はプログラミング言語 TL/1 の言語仕様のまとめです。 雑誌やウェブ上にある説明などを元にして私の解釈や曖昧箇所の指摘を加えて仕様の体裁に再構成したものです。
説明のために元資料にない用語を使う場合もあります。
この文章中で「未定義」としている箇所は、元資料から判断がつかないことを陽に示したものです。
構文図も用意していますが、識別子の解釈に独特な部分があるので文章の方を先に読んでから構文図を参考にした方がよいでしょう。
パーセント記号 (%) から次の改行までは無視され、プログラム的には空白文字と同等の意味しか持ちません。 プログラムの説明などを記述するのに使ってください。
英文字で始まり、後続する英数字 0 個以上で構成されます。 識別子を構成しない文字 (空白や記号) の直前までを 1 個の語として解釈します。 大文字と小文字は区別しません。 識別子の長さに上限はありません。
予約語、手続き名、関数名、大域単変数名、大域配列名、小域単変数名、小域配列名が同じ綴りを持つ可能性があります。 宣言時に既存の名前と重複しないかチェックされません。 宣言以外の箇所では処理系は以下の順序で名前の検索を試み、最初に発見された属性の識別子として解釈します。
4 種類の表現方法があります。
TL/1 で直接的に扱える数値はバイトサイズであり、ゆえにプログラム中で現れる数値表現は常に 0〜255 の範囲内です。
構文中で真偽値を要求している箇所においては 255 を真、それ以外の値は偽と解釈します。 真偽値を返す関数は真として 255 を、偽として 0 を返します。
0〜9 の文字を 1 つ以上並べて 0〜255 の範囲を数を表現できます。
頭に余計な 0 を付けた場合 (例えば 02 のような) の挙動は未定義です。
記号 $ に 0〜9 または a〜f で十六進表現で 0〜255 を表します。a〜f は大文字 (A〜F) を使っても同じ意味です。
記号 $ の後に空白文字を入れてはいけません。
十六進で二桁にするために余計な 0 を頭に付けること (例えば $0A のような) は許されますが、それよりも多くの 0 を頭に付けた場合 (例えば $002) の挙動は未定義です。
クオートで挟まれた一文字で表現します。 その一文字のアスキーコードと同じ数値が書かれたものと見做されます。
'A' % 65 と書いたのと同じ意味
識別子 TRUE もしくは FALSE で表します。 TRUE は $FF 、 FALSE は 0 と同じです。
その他、演算子や構文には記号を用いますが、後述する構文の説明の中で取り上げます。
語の間に挿入することが出来ます。 語の区切りとしては 1 個あれば充分ですが、何個連続しても意味は変わりませんので外観を整えるために活用して下さい。 空白文字と解釈する文字は以下の通りです。
使い方に習慣的な意味がある場合もあり、それについては後述します。
プログラムは以下の順序で構成されます。
それぞれの内容は以下のようになります。
プログラム中で使用する手続き名を予約語 PROC に続けて書きます。 手続き名が複数の場合はカンマで区切ります。
PROC , , ...
手続きが 0 個の場合は宣言を省略します。
プログラム中で使用する関数名を予約語 FUNC に続けて書きます。 関数名が複数の場合はカンマで区切ります。
FUNC , , ...
関数を 0 個の場合は宣言を省略します。
プログラム全体で使用する単変数名を予約語 VAR に続けて書きます。 大域単変数名が複数の場合はカンマで区切ります。
VAR , , ...
大域単変数が 0 個の場合は宣言を省略します。
後述の大域配列と合計して 256 バイト以内である必要があります。 更に副プログラムを呼出す場合は 2 バイトの余地が必要 (大域単変数と大域配列を合計した大きさが 254 バイト以下になる必要がある) です。
プログラム全体で使用する配列名とその大きさを予約語 ARRAY に続けて書きます。 配列の大きさは配列名の後に角括弧で囲んだ数値で表します。 配列名が複数の場合はカンマで区切ります。
ARRAY [ ] , [ ] , [ ] ...
配列の大きさは配列の添字の最大値です。 例えば A[10] と宣言した配列に対しては 0〜10 の添字で安全に参照できることを意味します。 C などのように配列の要素数ではないことに注意してください。
予約語 BEGIN と END で狭んだ 0 個以上の実行文から成ります。
副プログラムとは手続きか関数です。 以下のような順序で構成されます。
これが副プログラムの個数分だけ繰返されます。
副プログラムの定義は宣言の順序と一致しなくてもかまいません。
プログラムの最初の手続き名宣言か関数名宣言で宣言した名前です。
丸括弧で囲まれた複数の識別子です。 識別子が複数の場合はカンマで区切ります。
(, , ...)
仮引数はいわゆる値渡しのみです。 呼出し時の実引数で初期化される点が異なるだけの小域単変数です。 (暗黙の小域単変数宣言)
仮引数が 0 個の場合は丸括弧ごと省略することも出来ます。
当該の副プログラム内だけで参照可能な単変数を宣言します。 形式は大域単変数と同じです。
小域単変数が 0 個の場合は宣言を省略します。
大域単変数・大域配列と同様に、小域単変数と小域配列を合せた大きさが 256 バイト以下である必要があります。
当該の副プログラム内だけで参照可能な配列を宣言します。 形式は大域配列と同じです。
文括弧で 0 個以上の実行文を囲うことで 1 個の実行文にまとめることが出来ます。
BEGIN END
{
}[
](
)まとめられた文は複文と呼ばれ、あたかもひとつの実行文であるかのように振舞いますので、以降の説明で実行文が現れることが出来る箇所のどこにでも現れることが出来ます。 囲まれた実行文が 0 個の複文は空文と呼びます。
どの括弧記号を使っても同じ意味ですが、必ず対応する閉じ括弧で閉じる必要があります。 例えば「{」で始めて「]」で閉じるような使い方は出来ません。
複文中のそれぞれの実行文を区切るために、空白文字として無視されるセミコロン (;) を書く習慣があります。
主プログラム、副プログラムの本文も複文の一種ではありますが、上記で示した通り BEGIN と END で挟む必要があり、他の括弧は使えません。
実行を停止してモニタに飛びます。
主プログラムの最後には自動的に挿入されるので書かなくてもよいですが、主プログラム/副プログラムの任意の場所に書けます。
手続き、または関数から復帰します。
RETURN
RETURN
手続きから復帰する場合には式を持たない書式で、関数から復帰する場合は 1 個の式が続く書式で書きます。
手続きの最後には自動的に挿入されるので書かなくてもよいですが、手続き中のどこででも使用することが出来ます。 (一部の処理系では FOR ループ内で使ってはいけません。)
関数の定義内では必ず 1 つ以上使う必要があります。 (コンパイル時にチェックされないことに注意してください。 また、 FOR ループ内で使ってはいけません。)
関数内で RETURN 文を通過せずに終端 (関数の定義の終りを表す END) に行き当たった場合の挙動は未定義です。
単変数の値を変化させながら繰返しする処理を表します。
FOR := TO DO
FOR := DOWNTO DO
カウント用の
に式の値を代入し、 を 1 ずつ増加または減少させながら実行文を繰返します。TO を用いたとき +1 、 DOWNTO を用いたときは -1 です。 (一部の処理系では DOWNTO を使えません。)
の増分は、REPEAT UNTIL
の値が真値になるまで を繰返し実行します。 は 0 個以上の実行文を並べたものです。
WHILE DO
の値が偽ならば を実行せずに次の処理へ移ります。
式の値が真の場合は
を実行して再び の評価に戻ります。IF THEN
IF THEN ELSE
の値が真なら を実行します。
ELSE 節が省略されていないならば を実行します。
の値が偽であり式の値が偽であり ELSE 節が省略されているならば何もせずに次の処理へ移ります。 (一部の処理系では ELSE 節は使えません。)
CASE OF ... ELSE
の値を の値と比較して合致すれば を実行します。 その後は の次の処理に移ります。
合致しなければ、同様にして合致するまで次々と式と比較し、合致した式に対応した実行文を実行します。
式の箇所に予約語 ELSE が有った場合は無条件に合致したものとみなして を実行します。
CASE 文における ELSE 節は CASE 文の最後の条件であることを示すマーカーでもあるので省略することは出来ません。 ELSE 節に実行すべき実行文がない場合は空文を書いてください。
WRITE ( : )
式の値が表す出力装置に対して出力リストの内容を出力します。 数値と出力装置との対応付けについては未定義ですが、一般的に 0 はコンソール画面であるとされています。
出力リストは以下の出力要素からなり、ひとつ以上の場合はカンマで区切ります。
式を記述します。 十進数左詰めで出力します。
# (, )
の値を の桁数で十進右詰めで出力します。
"
"
ダブルクォーテーションで囲まれた文字列を出力します。
ASCII ( )
で与えられたアスキーコードに相当する文字を出力します。
SPACE ( )
で与えられた個数分の空白を出力します。 の値が 0 の場合は何も出力しません。
CRLF ( )
CRLF
で与えられた個数分の改行を出力します。 の値が 0 の場合は何も出力しません。
を省略した形式の場合は 1 個の改行を出力します。
HEX ( )
で与えられた値を十六進数 2 桁で出力します。
一部の処理系では使えません。
:=
, , ... , :=
の値を に代入します。
変数がカンマで区切られたリストの場合は
の値を左辺全ての変数に代入します。代入記号はコロンとイコールの 2 語から成っているのでコロンとイコールの間に空白文字が有っても代入記号として認識されますが、一般的には間を空けずに書きます。
手続きを呼出します。
( , , ... )
引数をもつ手続きでは実引数を与えて呼出します。 引数を持たない手続きを呼出す場合には丸括弧ごと省略した記法で呼出せますが、引数を持たない手続きを丸括弧を省略せずに記述した場合はエラーです。
FOO() % このような呼び方はエラー
実引数の渡し方はいわゆる値渡しに限定されているので、実引数が変数であっても手続きから戻ったときに値は変化しません。
機械語サブルーチンを呼出します。
CALL ( , , , , )
CALL ( , , , )
CALL ( , , )
CALL ( , )
各パラメータは以下の意味を持ちます。
アドレスの上位 8 ビット | |
アドレスの下位 8 ビット | |
アキュムレータに与える値 | |
80 系 CPU では H レジスタに与える値、 6502 系 CPU では X レジスタに与える値 | |
80 系 CPU では L レジスタに与える値、 6502 系 CPU では Y レジスタに与える値 |
, , は省略した形式がありますが、省略した場合はそれぞれの値は不定となります。
この手続きは一部の処理系では利用できません。
PC では STOP キー、 APPLE では cont-C が押されているか否かを検出し、押されていればモニタモードに戻ります。
変数はすべて 1 バイト長です。 以下の 4 種類があります。
VAR 宣言された英字で始まる英数字の列です。
大域、小域の区別があります。
[ ]
ARRAY 宣言された配列の 番目の要素です。
大域、小域の区別があります。
MEM ( , )
の値を上位、 の値を下位のアドレスとするメモリ内の 1 バイト。
PORT ( )
PC 版専用です。
N-BASIC の INP, OUT に相当します。 代入文の左辺にあれば OUT 、 右辺にあれば INP と同等の作用をします。
数値の項で示した 4 種類のいずれかの形式で定数を表します。
( , , ... , )
FUNC 宣言によって宣言された関数、または処理系が用意しているシステム関数 (後述) を呼出します。 引数のない関数を呼出す場合は関数名のみで呼出せます。 引数がない関数を括弧付きの書式で呼出そうとした場合はエラーです。
FOO := BAR() % このような呼び方はエラー
1 バイト同士の掛け算の結果は 2 バイトになり得ますが、式の中では下位 1 バイトしか表現されません。 上位 1 バイトは専用の場所に格納されており、 MHIGH 関数で取出すことが出来ます。
割り算すると商が返りますが、同時に余が計算されて専用の場所に格納されており、 MOD 関数で取出すことが出来ます。
RND ( )
1 以上
以下の一様乱数を返します。GET ( )
が表す入力装置から 1 文字を入力し、そのアスキーコードの値を返します。
数値と入力装置の対応付けは未定義ですが、一般に 0 はキーボードであるようです。
READ ( )
の値に対応する入力装置から十進数を 1 つ入力し、その値を返します。 RUBOUT コードは区切り記号とみなされます。
NOT ( )
1 の補数を返します。 後述の COM と同じです。
NEG ( )
2 の補数を返します。
COM ( )
1 の補数を返します。
LSR ( )
1 ビット右シフトします。 最上位ビットには 0 が入り、最下位ビットはキャリーに入ります。
ASR ( )
1 ビット右シフトします。 最上位ビットは変化せず、最下位ビットはキャリーに入ります。
ASL ( )
1 ビット左シフトします。 最下位ビットには 0 が入り、最上位ビットはキャリーに入ります。
ROR ( )
1 ビット右シフトします。 キャリーは最上位ビットに入り、最下位ビットはキャリーに入ります。
ROL ( )
1 ビット左シフトします。 キャリーは最下位ビットに入り、最上位ビットはキャリーに入ります。
USR ( , , , , )
USR ( , , , )
USR ( , , )
USR ( , )
CALL 文と機能は同じですが、機械語サブルーチン実行後のアキュムレータの値を返却値として返します。
処理系によっては使えません。
RDHEX ( )
入力装置から十六進数1桁を入力します。
処理系によっては使えません。
RRC ( )
キャリーを経由せずに式の値を右に 1 ビットシフトします。 最下位ビットは最上位に入ります。
処理系によっては使えません。
RLC ( )
キャリーを経由せずに式の値を左に 1 ビットシフトします。 最上位ビットは最下位ビットに入ります。
処理系によっては使えません。
左右に 2 個の項をとって計算する演算子です。
演算子の優先順位は表の通りです。 優先順位の同じ演算子は左結合します。
① | 乗除算演算子 |
② | 加減算演算子 |
③ | 関係演算子 |
④ | 論理演算子 |
⑤ | キャリー付き加減算演算子 |
優先順位を変更したい場合は式括弧を使用します。 以下 3 種類の括弧が式括弧として使えますが標準的には丸括弧を用いることとします。
{
}[
](
)記号ではなく識別子の演算子については空白文字と解釈されるピリオドを両側に置く習慣があります。 私見ですが、関数呼出しとの区別をしやすくする工夫だと考えられます。
.AND.
* | 乗算 |
/ | 除算の商 |
+ | 加算 |
- | 減算 |
2 つの値を比較して真偽値を返します。 GT と LT は左右の数値を 2 の補数表現の符号付き二進数とみなして比較します。 その他の演算子は数を符号なし二進数と解釈します。
> | 大きい |
< | 小さい |
# | 等しくない |
= | 等しい |
GT | 大きい |
LT | 大きい |
AND | 論理積 |
OR | 論理和 |
EOR | 排他的論理和 |
二項を足した上でキャリーの値を足す、または二項を減算した上でキャリーの値を引く演算子です。
ADC | キャリー付き加算 |
SBC | ボロー付き減算 |
システム関数の一部がフラグを変化させることが明記されている他はどの処理がフラグを変化させるかは未定義です。 一般に加減算はフラグを変化させるようですが、処理系によっては配列要素へアクセスしたときに変化させてしまう場合もあるようです。 キャリー付き加減算は加減算の直後に限って使うのが安全であると考えられます。