Skip to content

yunkya2/bas2c-x68k

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

65 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

X-BASIC to C コンバータ bas2c (Python 版 & C++ 版)

概要

X680x0 の C Compiler PRO-68K (XC) には BAStoC (BC.X) というコンバータが付属していて、X-BASIC のソースコードを C 言語に変換してコンパイルすることで高速化できたのですが、 残念ながら 無償公開されている XC からは削除されています。

このプログラムは BC.X の代替を目指して、X-BASIC のソースコードを C 言語に変換する Python スクリプト、および C++ プログラムです。

Python 版はクロス開発環境 elf2x68k と組み合わせて利用することを想定していますが、MicroPython for X68000 上でも動作するので、実機上での変換もできないことはないです (ただしめちゃくちゃ遅いです…)。

C++ 版は、X680x0 実機環境での動作に対応するために Python 版のコードを元に開発しました。オリジナルの BC.X と比べるとかなり遅いですが、無償公開版 XC での BC.X の補完のために公開します。

使い方

以下の説明は Python 版についてのものです。 X680x0 実機、エミュレータで動作する C++ 版については、README-bas2c.txt を参照してください。

Python 版は Linux (WSL) や MSYS2 等、Python3 が動作する環境で使用します。

X-BASIC → C の変換

bas2c.py と bas2c.def をパスの通った同じディレクトリに置いて、以下のように実行します。

bas2c.py [<オプション>] [<BASICソースコード>.bas] [-o <Cソースコード>.c]

<Cソースコード> の指定を省略すると、BASICソースコードの拡張子を .c に変更したファイル名を使用します。 <BASICソースコード> の指定を省略すると、標準入力から読み込んだソースコードを変換して標準出力に出力します。

以下の <オプション> が指定できます。

  • -u
    • ソースコード中に X-BASIC の標準関数や外部関数にない関数呼び出しがあった場合、通常はそれをそのまま出力しますが (BC.X と同じ動作)、このオプションを指定するとエラーにします。
  • -n
    • 通常、変換後の C ソースコードはプログラムの開始時に b_init()、終了時に b_exit() を呼び出して X-BASIC としての初期化/終了処理を行いますが、このオプションを指定すると b_init() は呼ばず、 b_exit() の代わりに exit() を呼び出します。
    • b_init() は X-BASIC プログラムの初期状態に合わせるため以下のような処理を行い、b_exit() はここで変更した内容を変更前の状態に戻す処理を行っているようです。
      • 画面モードの 512×512 への切り替え
      • ファンクションキー表示モード設定
      • ファンクションキーや特殊キーの定義内容初期化
      • CTRL+C 押下時のベクタ設定
      • 乱数系列の初期化
      • カーソル表示の消去
    • -n を指定して出力したソースコードではこれらの処理を行わないため、例えば変換後のプログラムの終了時に画面が消去されて表示結果が見えなくなるのを防ぐことができる一方で、初期化が行われていることが前提の処理が正常に動作しなくなる場合があります。
      • 具体的には、以下のような現象が発生します。
        • キー入力でカーソルキーなどの特殊キーを認識しない (X-BASIC ではこれらのキーに特別なコントロールコードが割り当てられるため)
        • BASIC プログラム中、KEY コマンドでファンクションキーの内容を再定義すると、Human68k に戻ってもその内容が残ったままになる
  • -s
    • 変換後の C ソースコードの文字コードを Shift_JIS にします。デフォルトは UTF-8 です。
    • (変換前の X-BASIC ソースコードは、Shift_JIS か UTF-8 かを自動判別します)
    • MicroPython 上で実行する場合はこのオプション指定は意味を持ちません。入力ソースコードの文字コードがそのまま出力に使われます。
  • -b
    • 変換時に、式における X-BASIC と C 言語の仕様の違いに対する補正を行いません。 X-BASIC と BC.X の出力とで結果が異なるようなソースコードを変換する場合、通常は X-BASIC の仕様に合わせるような変換を行いますが、このオプションを指定すると BC.X の出力に近い結果が得られるようになります。
  • -v
    • 変換中の BASIC ソースコードを標準出力に表示します。
  • -c[TAB数]
    • 変換後の C ソースコードに BASIC の各行をコメントとして出力します。TAB数 はコメントのインデントに使うタブの数です。デフォルトは 7 です。

変換したコードのコンパイル

bas2c.py で変換したソースコードは、elf2x68k で以下のようにしてコンパイルできます。

m68k-xelf-gcc -o <実行ファイル名> <Cソースコード>.c -specs=xc.specs -lbas

変換後のソースコードのコンパイルを X68k 実機やエミュレータ上の XC で行う場合は、

CC /W <Cソースコード>

のように /W オプションを付けて、BASIC ライブラリをリンクするようにしてください。

関数引数や戻り値での str 型の扱いについて

X-BASIC の str 型(文字列型)変数を関数の引数や戻り値として使う場合、X-BASIC インタプリタではその変数の値が引数や戻り値として渡される(値渡し)一方、bas2c や BC.X で変換した C ソースコードではその変数への参照が渡される(参照渡し)という違いがあります。

この違いは、X-BASIC インタプリタと変換後の C ソースコードでの挙動の違いだけでなく、アプリケーションのクラッシュを含めた様々な問題を起こす可能性があります。 bas2c.py では、こうした問題の原因となる操作をエラーとして扱います。-b オプションを指定すると、エラーチェックを行わず BC.X と同様の出力を得ることができます。

関数の str 型引数

str 型変数を引数に取る関数は、BC.X/bas2c の出力ではその変数への参照が引数として渡されます。 このため、関数内で引数変数への代入を行うと、X-BASIC インタプリタでは呼び出し元の変数には影響を与えない一方で、BC.X の出力では呼び出し元の変数の値も変わってしまいます。

それだけではなく、X-BASIC インタプリタと BC.X の出力のそれぞれで、以下のような一見奇妙な挙動を示します。

  • X-BASIC インタプリタ
    • 関数の str 型引数への代入を行うと、その関数が呼び出されたときに渡された引数の文字数+1 文字までが格納できるようです。
  • BC.X の出力
    • 関数の str 型引数への代入を行うと、最大3文字までしか格納できません。更に、参照渡しのため関数呼び出し元の変数の値も変化します。
   10 str s="abcdef"
   20 sfunc(s)
   30 print "s=";s
   40 end
  100 func str sfunc(a;str)
  110 a="1234567890"
  120 print "a=";a
  130 endfunc

X-BASIC インタプリタでの実行結果:
a=1234567
s=abcdef

BC.X 出力の実行結果:
a=123
s=123

bas2c では、関数の str 型引数への代入をエラーとして扱います。

関数の str 型戻り値

str 型の値を返す関数は、BC.X/bas2c の出力では戻り値を格納した変数への参照を返します。 関数内で定義されるローカル変数は関数から戻ると解放されてしまうため、このような変数への参照を返すと、関数から戻った後に存在しない変数への参照となるため、アプリケーションがクラッシュする可能性があります。

   10 print sfunc()
   20 end
  100 func str sfunc()
  110 str s="abcdef"
  120 return(s)   /* return後に変数 s は解放されるため正しくない操作 */
  130 endfunc

bas2c では、str 型を返す関数でローカル変数を返すとエラーとして扱います。

インライン C 言語機能

BC.X には、X-BASIC プログラム中の #c から #endc で囲まれた行をそのまま C ソースコードとして出力する、という未公開機能があります。 bas2c.py でも同等の機能をサポートしています。

X-BASIC では実現できない処理を C のコードとして記述するのに利用できますが、この機能で出力されるコードは main 関数または func 命令で定義中の関数の中になるため、グローバル変数の定義や #include ディレクティブなど、関数定義の外でないと書けない処理には使用できないことに注意が必要です。

例:

X-BASIC プログラム (テキスト VRAM の プレーン0 への書き込み):

   10 #c
   20 {
   30 void B_SUPER(int);    /* #includeが使えないので個別にプロトタイプ宣言が必要 */
   40 B_SUPER(0);           /* スーパーバイザモードへ */
   50 }
   60 #endc
   70 for i=&HE00000 to &HE0FFFF
   80 poke(i,&HFF)
   90 next
  100 end
 1000 func poke(a,v)        /* アドレスaに値vを書き込む関数 */
 1010 #c
 1020 *(char *)a = v;
 1030 #endc
 1040 endfunc

変換後の C ソースコード

/******** program start ********/
void main(int b_argc, char *b_argv[])
{
        b_init();
{                           // #c - #endc の中がそのまま出力される
void B_SUPER(int);          //
B_SUPER(0);                 //
}                           //
        for (i = 0xE00000; i <= 0xE0FFFF; i++) {
                poke(i, 0xFF);
        }
b_exit(0);
}

/***************************/
int poke(int a, int v)
{
*(char *)a = v;             // #c - #endc の中がそのまま出力される
}

※ インライン C 言語で DOS コールや IOCS コールを使用した場合は、elf2x68k でのコンパイル時には -ldos -liocs 、XC でのコンパイルの際には /Y オプション (DOS/IOCSライブラリの使用) を指定してください。

bas2c.def について

bas2c.py は、X-BASIC の標準関数や外部関数を C 言語での関数名を変換するために、bas2c.def というファイルを使用します。 BC.X で使われていた *.DEF ファイル (BASIC.DEF, GRAPH.DEF,...) を 1 ファイルにまとめた形になっています。

ファイルは以下のようなフォーマットで記述されています。

[<グループ名>]

<BASIC関数の戻り値型> <BASIC関数名>(<BASIC関数引数型...>) : <C関数名>(<C関数引数型...>)
    :

<グループ名> は、関数呼び出しが使われた際に必要なヘッダファイルを include するために使われます。 例えば、[GRAPH] グループにある関数 (fill(), line()など) がプログラム中で使われると、変換後の C ソースコードの冒頭に

#include <graph.h>

が追加されます。 適切な C 関数用ヘッダファイルを用意して bas2c.def に関数定義を追加することで、ユーザーが独自の外部関数を追加することもできます。

(bas2.def の冒頭には最初のグループとして、特殊な変換規則を必要とする関数、ステートメントが定義されています。これらの変換規則は bas2c.py 内の変換ルーチンの記述と連携しているので変更しないようにしてください)

制約と注意事項

  • BC.X は XC ver.1 の頃から使われていることもあり、出力される C ソースコードの関数定義が (いわゆる) K&R スタイルで書かれていました。 bas2c.py は XC ver.2 や gcc での利用を前提として関数定義を ANSI スタイルで出力します。 その他、配列の初期化などでも BC.X と異なるコードを出力します。
  • X-BASIC と C 言語とでは演算子の優先順位や論理演算の値が異なります(例えば、真を表す値は X-BASIC では -1 ですが C 言語では 1 です)。 BC.X はこの違いを考慮せずそのまま C 言語に変換していたため、式の計算結果が X-BASIC と異なることがありました。 bas2c.py では、変換時に必要に応じてカッコを補うことなどにより計算結果が変わらないようにしています。 この挙動は -b オプションによって変更できます。

ライセンス

bas2c.py は MIT ライセンスとします。