Return of Very Tiny Language
- 2024-11-16 Jun Mizutani
rvtl, rvtl64 はLinux用の Tiny Basic 系のプログラミング環境です。アセンブラで作成している ため、10-30キロバイトほどの非常に小さいプログラムですが、対話的に操作 できる (REPL:read-eval-print loop) 豊富な機能を持っています。アセンブラで作成していますが、x86、x86_64、ARM、ARM64、PowerPC とほとんどのCPUに対応しています。 日本語のコメントつきのアセンブリソース入りです。
BASIC言語系のインタプリタ、エディタ、コマンドラインのヒストリ機能と ファイル名補完、組み込みコマンドとしてLinuxの基本コマンド(ls, cat, cd, mkdir, rm, mount, unmount, chmod, mv, rmdir, sync, pwd) や上級者向けコマンド (chroot, pivot_root, swapon, swapoff, exec)、そしてグラフィックで比較的 簡単にゲームを作成できるようにフレームバッファに対するパターン転送 (スプライト描画)、ライン描画などのコマンドも用意されています。 擬似乱数の生成には高速で上質な Mersenne Twister を使用しています。 Linux カーネルさえあれば、ディストリビューションの種類に関係なく 実行できます。
rvtl は特殊な文法 (基本的にすべて代入文) を持つ CASUAL や VTL の子孫と なる言語です。 特殊といっても、日本では、GAME III が有名です。古くから 趣味でプログラミングしていた人には懐かしい言語ではないかと思います。 ラベルを使えるように文法を拡張しています。文法が単純なためインタープリタ でも高速に動作します。CPUに依存しますが、1秒間に100万から500万命令程度は 実行できると思います。
64bit版(x86_64, Arm64) の詳細は こちらのページ(rvtl64.html)、 32bit版(arm, x86) の詳細は こちら(rvtl32.html)です。
ダウンロード
x86版 (2015/10/05)
Arm版 (2019/12/26)
x84_64版 (2015/10/05)
Arm64版 (2019/12/26)
RISC-V (RV64IM) 版 (2024/11/16)
PowerPC版 (2006/08/03)
64bit版(x86_64, Arm64, RISC-V) と 32bit版(x86, ARM) の違い
64bit版(x86_64, Arm64, RISC-V) では、変数はすべて64bitとまでの数値が代入可能です。 rvtl64 は、32bit版 の rvtl と異なり64bitの命令で作成されているため、64bit版の Linux 専用です。変数は 64bit の整数を格納することができ、約±900京、±9,223,372,036,854,775,807 といった10進数で約20桁という非常に 大きな値を扱うことができます。また64bitの配列も使用できます。メモリアドレスの扱い以外は 32bit版(x86, ARM) の rvtl に対して rvtl64 は上位互換です。
ラベル名は rvtl では11文字(バイト)までまで区別されますが、rvtl64では 23文字(バイト)まで区別されます。 それ以上の長さのラベル名を使うことは できますが、11文字(32bit)、23文字(64bit)を超える部分は区別されません。
?%=e 式の値を16進数16桁で出力 (64bit版専用) $%=e 式の値を8文字の文字コードとして出力(64bit版専用) A;n] 変数 A の値を先頭アドレスとする8バイト配列 (64bit版専用)
フレームバッファを操作する命令 |fb? はアドレスとして64bitを渡す必要がある ため、引数の指定方法が変更(配列に64ビットの値を渡す部分)されています。
更新履歴
- ver.3.05(32bit), ver.4.01(64bit) の変更点
- 機能追加
- <A
- Aの下位32bitを64bitにゼロ拡張する単項演算子 (32bit版では何もしない)
- !
- 直近のジャンプ (#=, !=) を実行した行番号を保持
- |zc
- rvtl 起動後のコマンド実行回数を % に返す
- |ve
- rvtl のバージョンを % に返す(64bit版は上位32bitに1)
- |vc
- CPU の種類を % に返す
- |rt
- コンソールをローカルエコー+Cookedモードに再設定
- 組み込みラインエディタは utf-8 の日本語の編集が可能となりました。
- 機能追加
- ver.3.03 から ver.3.04 の変更点
- Linuxカーネルの Address Space Layout Randomization に対応。
- ver.3.02a から ver.3.03 の変更点
- |ud を追加 (URL デコード)
- |ve で % にバージョン番号を返す。
- A*=e のコピーバイト数上限を 256KB に変更。
- A*=A の場合はコピーせずバイト数を%に返す。
- 文字列出力を効率化
- $*=A の文字数制限の撤廃
- ver.3.02 から ver.3.02a の変更点
- |zz (システムコール) で Segmentation fault を修正
- ver.3.01 から ver.3.02 の変更点
- カーネル2.6でフレームバッファが使用できないのを修正
- 外部コマンドの起動ができなかったのを修正
- 環境変数の読み込み「\\式」を追加
- サンプルプログラムの追加、修正
- ver.3.00 から ver.3.01 の変更点
- )=""、(=""、<=""、>="" の動作不良の修正
- 引数のファイルの再読み込み現象の修正
- 数値入力、文字列入力時のヒストリ、ファイル名補完の行頭ずれを修正
- A*="文字列" の機能追加
- サンプルプログラムの追加、修正
- ver.2.02 から ver.3.01 の変更点
- ラベル参照の不具合を修正
- 式のエラー表示を改善
- コマンドライン引数の一部を rvtl から参照可能とした。
- ファイル名先頭アドレスを指定するファイルの読み書きコマンドを追加
- 文字列のコピーと1行入力を追加
- 文字列のアドレスを引数とする組み込みコマンドを追加
- 点の描画コマンドを追加
- サンプルプログラムの整理
- ドキュメントの改訂
rvtlの文法の紹介
次のコードを見てください。大昔からプログラミングをしていた人以外は、 まったく意味が分からないと思います。 このような記号と数字ばかりの 変な言語にもそれなりの理由があります。その理由を知ればそれほど異常な 文法ではないことが分かってもらえると思います。
10 A=? 20 #=(A>100)*50 30 ?=10 40 #=60 50 ?=100 60 #=-1
このコードの意味は後ほど説明するとして、まず元になるBASIC言語から紹介します。
BASIC は1960年代に初心者向けに設計されたプログラミング言語で、1980年代 から1990年代にかけてよく使われてました。 BASIC言語は処理系ごとに拡張 された部分が多く、時代とともに変化していますが、基本的に各行の先頭に 行番号と呼ばれる数字を置き、その後ろに命令文を書きます。
行番号は、10、20、30、... というようにして、行と行の間に挿入できるように 番号付けするのが普通です。11、12、13、... でも500、600、900でも文法的 には問題ありません。 行番号の大きくなる順にソートされます。行番号は、 1行ずつしか入力を受け付けず、ほとんど修正もできないような貧弱な環境でも プログラムが作成できるように、行に番号を付けて大きくなる順にソートする ことで、長いプログラムに対して途中に行を挿入したり、削除するための仕組みです。
10 ' BASIC 20 PRINT "hello, world" 30 GOTO 20 40 END
この例では、最初の行番号10の行はコメントです。行番号20の行で、 「hello,worldと表示」します。 次の行番号30の行で、「行番号20の行に行く」 という内容を実行します。永遠に hello,world を表示し続けるプログラムと なります。行の先頭に行番号が付いている以外は特に違和感はないと思います。
同じことを rvtl で書きます。各行の動作は上のBASICの例と同じです。
10 : rvtl 20 "hello, world" 30 #=20 40 #=-1
rvtl の文法は、rvtl の遠い先祖である VTL という言語がもとになっています。 VTL は処理系のプログラムサイズが 768バイトという非常に小さいものであった ため文法が工夫されています。 「#」はナンバー記号(number sign)と呼ばれる 記号ですが、この記号を実行中の命令のある行番号を表す変数名とします。
「#=10」 のように行番号変数に代入すると、行番号10にある命令を実行する、 つまり GOTO 10 と同じ働きになります。 存在しない行番号のときは、それより 大きくて最も近い行番号に移動します。 「#=-1」 のように行番号が負の場合や、 実行する行がなければ終了します。「#=1」は行番号 1 からの実行なので、常に プログラムの先頭から実行して、BASIC のRUN コマンドと同じ「プログラムを 起動」という動作になります。「#=0」 のように行番号に 0 を指定した場合は、 先頭に飛ぶのではなく、「#=0」 の次の行に処理が移る仕様になっています。
また、数値を「?」に代入すると標準出力に数値が出力されます。「A=?」の ように変数に代入すると標準入力から入力された数値が変数に格納されます。 つまり「?」が数値の入出力に使われます。文字列の表示はダブルクォートで 囲んだ文字列が標準出力に出力されます。上の例では「"hello, world"」だけで hello,world を表示します。
「#=0」のときには次の行に移る機能を使うと if のように条件分岐が可能と なります。2行目の (A>100) という式は、変数Aの持つ値が100より大きい 場合は、真を表す1という数値となり、A が 100 以下の場合は、(A>100)は偽を 表す 0 となります。 結果として、変数Aの持つ値によって #=50 または #=0 となるため、条件分岐やループが可能です。
10 A=? 20 #=(A>100)*50 30 ?=10 40 #=60 50 ?=100 60 #=-1
このプログラムは rvtl でも実行して動作を確認することができます。
$ rvtl RVTL v.3.05arm 2015/10/05, (C)2003-2015 Jun Mizutani RVTL may be copied under the terms of the GNU General Public License. <40F9> 10 A=? <40F9> 20 #=(A>100)*50 <40F9> 30 ?=10 <40F9> 40 #=60 <40F9> 50 ?=100 <40F9> 60 #=-1 <40F9> 0 【リスト表示コマンド】 10 A=? 20 #=(A>100)*50 30 ?=10 40 #=60 50 ?=100 60 #=-1 <40F9> #=1 【実行コマンド】 99 10 <40F9> #=1 【実行コマンド】 200 100 <40F9> ~ 【終了コマンド】
「#=B*20+100」とすれば、Bの値で、120、140、160といった行番号まで処理を 移す switch - case 文も実現できます。 とはいっても読み難いプログラムに なってしまいます。 GAME III ではIF文やGOSUB、RETURNに相当する機能が拡張 され、rvtl ではラベルが使えるようになって、行番号にジャンプする必要は なくなっています。次の例は IF文の機能を持つ「;=」とラベルを使って書き 直したものです。
10 A=? 20 ;=A>100 ?=100 #=^skip 30 ?=10 40 ^skip 50 #=-1
このように変数名が記号となっているシステム変数というアイデアによって、 rvtl では色々な言語機能を実現しています。