中間言語化実験室


戻る


更新:04/05/15

04/03/02

 長らく、「ベタテキストのまま実行する」のが流儀だった超ミニBASICですが、部分的に中間言語化してみることにしました。中間言語化したインタプリタが安定して動くようになったら標準版のほうに成果を反映して行こうと思います。

 いままで中間言語化に躊躇していた最大の理由は、中間言語化すると LIST "<文字列>" による、文字列抽出リストができなくなることを恐れていたからなのですが、LISTコマンド実行の際、中間言語化された行の内容を一旦バッファに展開してから文字列の比較を行い、その行を出力するかしないか決めればいい、ということに気付きました。

 ただ、当面全部のキーワードを中間言語化するつもりはありません。中間言語用コードとして16進で$01〜$1Fの範囲のコードを割り当てようと思っています。この範囲のコードは普通のテキスト中に現れることはなく、即座に中間言語であると判断できるからです。$20以上のコードも中間言語として使用するとなると、いろいろ処理が面倒くさくなりそうなので。

 全部ではなく、部分的に中間言語化するとしたら何を中間言語化するのが効率的でしょうか? INITLCD文はプログラムの頭で一回実行するだけですから中間言語化してもほとんど意味がありません。PRINT文やLPRINT文は使用頻度は高いんですが、これらの文の速度はRS232Cや液晶の表示速度に制限されますので中間言語化してもあまり効果は期待できません。
 やはり制御構文を中間言語化すべきではないでしょうか。以下のアセンブラリストを見てください。これはVer1.02bの、whileループをスキップしている処理の部分を抜粋したものですが、見ていると絶対に中間言語化したくなりますよね。WとかHとかいちいち1文字づつ比較しているんです。これが、1バイトの比較命令一発になれば・・・
WHLSKP	MOV.B	#1,R2L			; SKIP LEVEL
WHLSLP	BSR	SKPQRL
	AND.B	R0L,R0L			; END OF LINE ?
	BEQ	WHLS2:8
	ADDS	#1,ER3
	CMP	#H'57,R0L		; 'W'
	BNE	WHLSLP:8

	MOV.B	@ER3,R0L
	CMP.B	#H'45,R0L		; 'E'
	BEQ	WHLSWE:8
	CMP.B	#H'48,R0L		; 'H'
	BNE	WHLSLP:8

	ADDS	#1,ER3
	MOV.B	@ER3,R0L
	CMP.B	#H'49,R0L		; 'I'
	BNE	WHLSLP:8
	ADDS	#1,ER3
	MOV.B	@ER3,R0L
	CMP.B	#H'4C,R0L		; 'L'
	BNE	WHLSLP:8
	ADDS	#1,ER3
	MOV.B	@ER3,R0L
	CMP.B	#H'45,R0L		; 'E'
	BNE	WHLSLP:8
	ADDS	#1,ER3
	INC.B	R2L
	BNE	WHLSLP:8
	BRA	OUTWRK


 と、いうわけで以下の制御構文を中間コード化することにしました。

キーワード

中間コード

懐古コーナー

 ・・・思い出しました。昔、最初にMSX版超ミニBASICを作ったときのことを。
 構造化IF構文で、ELSEとかENDIFとかのキーワードを使わずに }{ 、 } にしたのは、それ以前に作ったH−FORTHを引きずっていたってのもありますが、なによりもインタプリタですから、4文字、5文字のキーワードよりも }{ 2文字あるいは } 1文字の方が早く比較できるから、と思ったからなのでした。


IF(

$17

}{

$18

}

$19

FOR

$1A

NEXT

$1B

REPEAT

$1C

UNTIL

$1D

WHILE

$1E

WEND

$1F



 それと、中間言語化と合わせて、結構大きな「構造改革」もしています。

 構造化IF構文や、BREAK文は、実行してはいけない区間をスキップしますが、以前のバージョンは構造化IF構文はスキップ処理をインタプリタのメインエンジン(Ver1.02cのソースで言えば3137行目あたりから)が行っていたのですが、BREAK文は自前でスキップ処理を行うという、チグハグな構成になっていました。

 なんでこういう構成になっているかですが、最初に作ったMSX版には構造化IF構文しかなく、H8版は木に竹を継ぐような感じでBREAK文が後付けされたのです。

 似たようなスキップ処理が別々の場所に存在するのもムダな気がしますし、単なる「文」に過ぎないBREAK文にインタプリタの中核とほぼ同等の機能を担わせるのも変だと思うので、スキップ処理はインタプリタのメインエンジンで一括して行うようにしました。

・ベンチマーク

 中間言語化版と標準版(Ver1.02c)のスピードを比べてみました。実行環境はH8/3048 16MHzです。

テストプログラム

実行時間

標準版

中間言語化版

10 FOR I=0 TO 1000000
20 NEXT
30 ?"END."

34秒

29秒

10 A=0:B=0
20 FOR I=0 TO 100000
30 IF(I AND 1) {
40   A=A+1
50 }{
60   B=B+1
70 }
80 NEXT
90 ?"END."

41秒

36秒

10 FOR I=0 TO 100000
20 REPEAT
30   BREAK
40 UNTIL 1
50 NEXT
60 ?"END."

18秒

17秒


・ダウンロード

超ミニBASIC for H8/3048 中間言語化バージョン(03/02版)をダウンロードする


ダウンロードしたファイルは+Lhaca、Winzipなどの解凍ソフトで解凍してください。
※このアーカイブ中にはソースプログラム(icode.mar,h8_3048.inc,cmb.inc)と実行プログラム(icode.mot)しか入っていません。このページを見ていない人には使い方がわからないので、他で再配布しないでください。

04/04/11

 中間言語化第2弾です。新たに以下の二点の変更を行いました。

・メモリやポートを操作する文・関数の中間言語化

 具体的にはPOKE文、BCLR文、BSET文、PEEK関数、PEEK%関数、PEEK#関数、BTST関数、BTST@関数が対象です。組み込み制御用途ではこれらの文、関数の使用頻度が高いので、中間言語化の効果は大きいと思われます。
 中間コードは以下のものが追加されました。

キーワード

中間コード

BTST

$12

PEEK

$13

BCLR

$14

BSET

$15

POKE

$16


 PEEK%関数やBTST@関数などは、それぞれ $13 $25、$12 $40 のように中間コード化されます。

・連続する半角空白の圧縮

 半角空白が2個、4個、8個連続していた場合、それぞれを1バイトのコードに圧縮します。これにより、プログラム中に余計な空白を入れることによる速度低下やメモリ不足などのデメリットがかなり解消されますので、心おきなくプログラムを見やすくするためのインデント(字下げ)ができます。
 なお、注釈中の空白は圧縮されますが、”(ダブルクオート)で囲まれている空白は圧縮しません。

 圧縮された空白のコードは以下のようになります。

空白2個

$01

空白4個

$02

空白8個

$03



・ベンチマーク

 標準版(Ver1.02c)、3月2日版、4月11日版のスピードを比較してみました。実行環境はH8/3048 16MHzです。

テストプログラム

実行時間

標準版

3月2日版

4月11日版

10 S=0
20 POKE $FFF800,0,0
30 FOR I=1 to 100000
40   BSET $FFF800,BTST@($FFF801,0)
50   POKE $FFF801,PEEK($FFF801)+1
60   S=S+PEEK%($FFF800)
70 NEXT
80 ?S

84秒

82秒

78秒


・ダウンロード

超ミニBASIC for H8/3048 中間言語化バージョン(04/11版)をダウンロードする


ダウンロードしたファイルは+Lhaca、Winzipなどの解凍ソフトで解凍してください。
※このアーカイブ中にはソースプログラム(icode2.mar,h8_3048.inc,cmb.inc)と実行プログラム(icode2.mot)しか入っていません。このページを見ていない人には使い方がわからないので、他で再配布しないでください。

04/05/15

 中間言語化第3弾です。数値定数のバイナリ化を行いました。
 いままで、数値定数もアスキーコードのままメモリに格納していたわけですが、たとえば "1000" なんてアスキー列を、ループ中で毎回 $03E8 とかに変換しているのかと思うと、その効率の悪さに頭が痛くなってきそうです。特にメモリやI/Oを操作するプログラムの場合、6桁の16進数が頻繁に出てくるわけで、それを入力した時点でバイナリ化しておけばかなりスピードアップすると思われます。

 中間コードは以下のものが追加されました。

値の範囲

中間コード

10進定数

10〜255

$04 + データ1バイト

256〜65535

$05 + データ2バイト

65536以上

$06 + データ4バイト

16進定数

$00〜$FF

$07 + データ1バイト

$100〜$FFFF

$08 + データ2バイト

$10000以上

$09 + データ4バイト


 たとえば、10進数で 100 なら、 $04 $64 という2バイトの中間コードになります。16進数で $ABCD なら $08 $AB $CD の3バイトの中間コードになります。なお、マイナスは中間言語化されません。たとえば -100 は、最初のマイナス記号はアスキーコードのままで、 $2D $04 $64 となります。
 また、10進数で1桁、すなわち0〜9の範囲の数値は中間コード化されません。10進1桁ならアスキーコードのままだと1バイトですが中間コード化した場合2バイトに膨れ上がってしまいます。1桁だけならアスキー→バイナリの変換もほとんど時間はかからないので中間コード化しないようにしました。

 リスト表示の際、見やすくするために上位桁に0を補って桁をそろえることもあると思いますが、数値を中間コードする新バージョンでは上位の不要な0は自動的に削除されてしまいます。そのような場合、代わりに半角空白を補ってください。

旧バージョンではできた書き方
(新バージョンだと桁がそろわない)

新バージョンでの書き方

10 A=$0800
20 B=$1000
LIST
        10 A=$800
        20 B=$1000
OK
10 A= $800
20 B=$1000
LIST
        10 A= $800
        20 B=$1000
OK


・ベンチマーク

 標準版(Ver1.02c)、3月2日版、4月11日版、5月15日版のスピードを比較してみました。実行環境はH8/3048 16MHzです。

テストプログラム

実行時間

標準版

3月2日版

4月11日版

5月15日版

10 S=0
20 POKE $FFF800,0,0
30 FOR I=1 to 100000
40   BSET $FFF800,BTST@($FFF801,0)
50   POKE $FFF801,PEEK($FFF801)+1
60   S=S+PEEK%($FFF800)
70 NEXT
80 ?S

84秒

82秒

78秒

65秒


・ダウンロード

超ミニBASIC for H8/3048 中間言語化バージョン(05/15版)をダウンロードする


ダウンロードしたファイルは+Lhaca、Winzipなどの解凍ソフトで解凍してください。
※このアーカイブ中にはソースプログラム(icode3.mar,h8_3048.inc,cmb.inc)と実行プログラム(icode3.mot)しか入っていません。このページを見ていない人には使い方がわからないので、他で再配布しないでください。

・今後の予定

 これ以上の、「簡単なわりに効果の大きい中間言語化」はいまのところ思いつかないので、中間言語化の実験は一旦5月15日版で終わりとします。これ以上中間言語化を進めるなら、いっそのことBASICコンパイラにでもしたほうがいいような気がしますし。
 もちろん、なにがなんでももう絶対これ以上中間言語化は進めない、ってわけではなく、なにか効果的な中間言語化を思いついたら実施します。

 とりあえず、現時点での成果を標準版(3664版やSH版にも)に反映しようと思います。あと、いつになるかわかりませんが、「中間言語対応のマルチスレッド版」も作りたいです。