中間コードにコンパイルしてから実行するという構造もそこからいただきました。
「ぺけ-BASIC」は物理的に速くないX68000上で、出来るだけ速くX-BASICを動かそうとする優秀なプログラムですが、いかんせんオールアセンブラな上にメンテナンス性が考慮されてない記述だったため、解析はきわめて難しいものでした。
(68000というMPUやアセンブラHASに強く依存する記述や、定数化してない即値記述が非常に多いなど。)
原作者の方が「バグ取り以外はしない」とおっしゃられていましたが、実際のところ「それ以外出来ない」状況にあったというのが真実でしょう。
少しでもいじろうもんなら他の部分に影響を与える、そんな、ある意味「絶妙なバランス」でした。
今回の開発ではX-BASIC/68だけでなくぺけ-BASIC上でも動作確認をしましたが、
いろいろとバグを発見しました。今更ではありますが、ここに公開しておきます。
もちろん、iOS版ではすべて解消しています。
-
floatの変数初期化サイズが足りていない
要するに宣言しただけでは0.0にならないと言うことです。
float変数を使う場合、明示的に初期化しないと正しくない結果をもたらします。
-
float定数のみ f1 * -f2で「無効な命令が発生する」
「例」4.321*-5.01
X-BASIC/68はOK
int/charはOK
-
単項演算子が連続すると式エラーが出る
not -/-notなど
float/int/charすべて
X-BASIC/68はOK
()を付ければ回避できる
-
usingの結果および書式指定の方法がX-BASIC/68と異なる
"+##"の結果
"\.#"の結果(整数部なしで\をつける)
^^^^^指定時に,を付けたらエラーになる
X-BASIC/68 ぺけ -^^^^^ ^^^^^- +** **+
-
printの数値表示時の桁位置が違う
X-BASIC/68 符号(+の時は空白)+数値+空白1文字 ぺけ 空白+符号付き数値+空白
-
e表現floatが使えない
3.14e10など
-
then {またはelse {の後に:/*と書くとエラーになる
コメントを書く場合、:なしで/*と書かなければならない
内部的にはマルチステートメントの処理に問題がある
X-BASIC/68でもelse {で発生する。
-
tan(pi(1.0/2.0))の計算結果がおかしい
6.2184311638237E+015となってしまう。
正解は1.6331239353195E+016
X-BASIC/68も同じなので、XM6 typeG/xm6i、もしくはfloat2.xのバグかもしれない。ちなみに、sin()/cos()で計算すると正しい。
sin(pi/2)/cos(pi/2)=1.6331239353195E+016
以下も正しい。
tan(pi/4)=1
tan(pi/8)=0.4142... -
配列を引数に持つ関数の中で、子関数にその配列を渡すときエラーが発生する
func testFunc(n;int,s(4,1);str)
引数配列の要素数は省略が可能=呼び出し時に確定されるため、コンパイル時には未定のままになっています。 ところが、その確定処理が抜けているためエラーが発生しています。
~
subFunc(s) :// ここでエラーが発生する
endfunc
func subFunc(s(4,1);str)
~
endfunc
なお、要素数を省略してない場合もエラーになります。
-
関数の引数名が関数名と同じだったとき、func文で変数二重宣言エラーが発生する
関数の引数名は関数名と同じにしないでください。
なおこれはX-BASIC/68では発生しませんが、ぺけ-BASICで発生します。
バグというより仕様です。両者の数少ない非互換部分の1つです。
-
func message(n; int , rt; int)のように、引数型を示す;の後にスペースを入れると内部エラーが発生する。
スペースを取るとOK。X-BASIC/68では発生しない。
- using "~"の後ろにスペースを置くとエラーが発生する
using "###";a /* OK
X-BASIC/68では発生しない。
using "###" ;a /* エラー
- 0除算をしてもエラーが発生しないことがある
例えば、以下の時にそうなります。print "1/0=";1/0 /* エラーが発生するはずなのに1と表示される
どういう条件ではエラーが発生しないのかまでは突き止めてませんが、どうも整数はエラーになっても実数演算で0になるときはエラーにならないような感じです。
/*
int a=0
int b=0*(1-1#/(a*a))
print "b=";b /* 2147483647と表示される
/* ちなみに
print 0*(1-1#/(a*a)) /* #NANと表示される
int b=0*(1-1/(a*a)) /* とするとエラーが発生する
なお、X-BASIC/68ではすべてエラーになります。
以下は、ソース内に見つけた不具合らしき部分です。実動作では確認してません。
-
以下の物が32767個を超えると異常動作を起こす
変数の個数(グローバル/ローカル別)
ラベル最大数
行番号の実利用数
-
行番号に65535を使うと異常動作する
-
func で配列引数を取った時、dim(n,m の後に)以外の文字があると其の分ワークが無駄に消費される
たぶん暴走はしないけど、おかしい
-
配列の初期化
{}の個数が宣言した要素数より多い時、中間コードエリアを破壊する
一応後でエラーを出す処理は入っているが、そこまで行くまでに(メモリ内容を破壊して)異常動作を起こす可能性がある
-
関数で配列引数のとき3次元以上の配列を与えてもエラーが発生しない
次元判定に使うレジスタを間違っている
-
配列への代入がおかしい
レジスタの参照を間違っているので。
たぶん ぺけ-BASIC はたまたま動いてるだけ。
-
文字列定数加算をしたとき
その合計サイズが256バイトを超えるとワークを破壊する
その領域を別の文字列変数がすでに使っている場合、その内容が化ける
加算を繰り返すと其の都度新しいワークを割り付けるため、メモリ不足に陥りやすい
-
拡張配列を関数の引数にしたとき、要素数テーブルを過剰にクリアしてしまう
-
配列宣言で、閉じ括弧がなくソースが終わったときにハングアップする。
多次元配列で,の後がなくソースが終わったときも同様。
この場で感謝いたします。
ーーーーーーーーーーーーーーーーーーーーーーーー
おまけ
X-BASIC/68のバグ。
・引数なし関数の定義をするとき、func f( ) とカッコ内にスペースが有るとき、呼び出し側で「式の記述が間違っています」エラーが発生する
f() :/* ここで発生
引数なしの時は ()としなければならない。
ぺけーBASICでは発生しない。
・プログラムファイルの終端を&h1aだけで判断しているため、それがないファイルを読み込ませた時、プログラムにゴミが入ることがある。
変な行が途中に入ってエラーが発生する事が多い。
これはバグというより「仕様」。
ぺけBの中の人です。たまたま見つけて嬉しくなってコメントしてしまいました。
返信削除こんなにバグをいっぱい見つけてくださってありがとうございますw 言語実装について何の勉強もしてない素人が見よう見まねで書いたものなので、メンテナンス性が低いのは勘弁してやってください。これ書いた当時は若さも手伝ってコードがほぼ全て頭に入っていたので、メンテナンス性とか全く気にする必要がなかったというのが自慢……にはならないですね(苦笑
using や float まわりは完全に手抜きです。ゲームではあんまり使わないだろうという……。
個数・サイズの制限は速度優先という判断でしたが、チェックすらしてないのは怒られるだろうなあと内心思ってましたw
「バグ取り以外はしない」というのは、ステートメントを増やしにくい(X-BASICの予約語に対する完全ハッシュをでっちあげたので、予約語を増やしたくない)というのもありましたが、当時一部で流行り始めたオブジェクト指向型にしてほしいみたいなムチャぶりがあったりしたので、そーゆーのは勘弁して下さいという予防線みたいなもんでした。今は……MC68000 アセンブラをだいぶ忘れてしまったので、1ヶ月くらいリハビリしないと手も足も出ません……。
まさか、中の人に降臨いただけるなんて。
返信削除ありがとうございます。
開発にあたって連絡を差し上げたほうが良いのかとも思いましたが、連絡先を見つけられませんでしたので、あしからずご了承ください。
動作検証だけでなく、動作フローを考えるのに、XBのソースを大いに参考にさせていただきました。本当に感謝しています。
「ここが違う」とか「こうした方がいい」とかありましたら、遠慮なくお知らせください。