2024年3月24日日曜日

6800用GAMEインタプリタとコンパイラの6809への移植がようやく完成

 6800用のGAMEインタプリタとコンパイラの6809への移植がようやく完成しました

時代錯誤ではありますが、折を見ながら昔のマイコン時代に使用していたGAME言語を6809に移植する試みを続けていまして、今までに2回報告しています。

・2021年5月19日のブログ「6800用のGAMEインタプリタとコンパイラを6809に移植」https://www.blogger.com/blog/post/edit/1662007451717538019/285975797531168690

・2019年5月8日のブログ「6802基板でGAME68コンパイラを走らせる」https://www.blogger.com/blog/post/edit/1662007451717538019/2314144451913456377

できる限りオリジナルから改変せずに自作の6802/6809両用カードにインタプリタとコンパイラを移植することを目指したので、既に他の方々が実践されているような高速化・高機能化とは無縁ですが、移植過程の経験とソースを得られることが目標でした。

上記2回の報告では、とりあえず動作したというレベルでしたので、何とか完成させたいと折を見ながら取り組んできましたが、ようやく完成と言ってもよいものができあがりましたので紹介するとともに作成したファイルを公開します。


作成に使用した自作マイコンですが、以前のブログで紹介したものと同じ6802/6809両用カードを使用しています。(見出しに画像を表示させるために以前と同じ画像を張り付けてあります。)


6802/6809両用カード


1.GAME3インタプリタ

まずインタプリタですが、ASCII誌に連載されたオリジナルのGAME3に作者の大西氏が行編集機能を追加されたものを使用しています。

オリジナルのソースに追加する必要があるのはI/O関係の1文字入力、1文字出力、ブレーク判定ルーチンのみで、変更点はRUB,DELコード、RAM末アドレスなどですが、これらについてはソースプログラムを添付しますのでそれを見ていただければ分かると思います。


2.GAME3コンパイラ

前回のブログで紹介しましたが、ASCII誌に掲載された松島義明さんのH68/TR・TV用の「GAME68コンパイラ」を移植しました。(松島さんには申し訳ないのですが、名称をGAME3コンパイラに変更させていただきました。)

これはGAME3自身で記述されており、6809に移植しやすいということで使用しました。

基本的にはオリジナルのままで動作しますが、インタプリタ中のルーチンを使用していますので、上記のインタプリタとセットで使用します。

変更点は220行のモニタへのアドレス$F0B1と、オリジナルのままではコンパイル結果のバイナリを実行後に暴走しますので、80行の末尾にA:0)=$39を追加した2点のみです。


3.GAME9インタプリタ

6800用GAME3インタプリタを元にして、6809用GAME9インタプリタを作成するわけですが、以前のブログで報告しましたように、基本的にはソースプログラムが公開されていますのでその6800の命令を6809の命令に置き換えるだけです。

置き換えに当たってはスタックポインタをインデックスポインタとして使用している箇所や比較命令CPXを1バイトスキップに利用している箇所に注意するだけで良いはずなのですが、何と終了判定にプログラムコード中の$00を利用している箇所があり、6800と6809では命令の長さが異なるために判定位置がずれてしまうことに気づかず、最後まで悩まされました。

以上に注意しながらインタプリタのソースを書き換えた結果、正常に動作させることができました。出来上がったものは基本的にただ6800の命令を6809の命令に置き換えただけですので、他の方々が移植されたものとは速度や機能の面で劣りますが、とにかく正常に動作するソースが作成できたということで良しとします。


4.GAME9コンパイラ

続いて「GAME68コンパイラ」の移植に取り組みましたが、これも以前のブログで報告しましたが、まずランタイムルーチンのバイナリを逆アセンブルしてソースを起こし、それを6809の命令に書き換えました。

続いてコンパイラの移植ですが、まず、GAME自身で記述されているコンパイラのリスト中の、インタプリタ中のルーチンを呼んでいるアドレスを書き換え、次に、6800の命令コードを発行している箇所を見つけて6809の命令に置き換えました。 同じバイト数で置き換えられるものは単純に置き換えられるのですが、バイト数が変わる場合はそれに応じて、その周辺を書き換える必要がありました。

最後に、多少なりとも6809らしいコードを出力して欲しいということで、AccAとAccBの両方を使用している箇所をAccDに置き換える等を試みましたが、全てを置き換えることはできませんでした。

結果として作成したコンパイラですが、いくつかのサンプルプログラムを実行して正常にコンパイルできていることを確認し、最後にコンパイラ自身をコンパイルしてみました。

その結果、得られたバイナリが正常に動作しましたし、さらにそのバイナリでもう一度コンパイルしてみたところ、そのバイナリも正常に動作しました。

ただし、GAME9コンパイラ自身をコンパイルする過程では、最終行までコンパイルした後にハングアップしてしまいましたが、調べてみるとインタプリタに戻るアドレスが書き込まれていませんでしたので、手作業で該当の2個所に$0103を書き込むことで正常に動作するオブジェクトが得られました。

(この現象はGAME3でも同様でしたので、元のGAME68コンパイラに原因があるのではないかと思っていますが、コンパイルせずにそのまま使用した場合には正常に動作するので、原因については良く分かりません。)


以上により得られたファイルは次のようです。

[1]GAME3

・GAME3EX インタプリタ

・GAME3C  コンパイラ(GAME言語で書かれたもの)

・GAME3CC コンパイラオブジェクト(GAME3Cを自身でコンパイルしたもの)

・RELSUB3  コンパイラ用ランタイムルーチン($2000-212Aだが移動可能)

コンパイル結果のオブジェクトの実行開始アドレスはランタイムルーチンの先頭アドレス+$012Bです。


[2]GAME9

・GAME9EX インタプリタ

・GAME9C  コンパイラ(GAME言語で書かれたもの)

・GAME9CC コンパイラオブジェクト(GAME9Cを自身でコンパイルしたもの)

・RELSUB9 コンパイラ用ランタイムルーチン($2000-2106だが移動可能)

ランタイムルーチンがGAME3用よりも小さいのですが、操作を統一するためにコンパイル結果の実行開始アドレスをGAME3と同じランタイムルーチンの先頭アドレス+$012Bに揃えてあります。


メモリマップです。
もちろんソースプログラムの位置は変更可能です。
コンパイラの方は、ランタイムやコンパイル結果の配置も変更可能です。



メモリマップ

コンパイラの動作速度を知るために、参考までに、インタプリタで300行弱のコンパイラプログラムを実行(コンパイル)した場合と、コンパイル済みのオブジェクトでコンパイルした時の時間を測ってみました。

(1)GAME3

 ・インタプリタでは、パス1終了までに4分30秒、パス2終了までに10分30秒

 ・コンパイラでは、パス1終了までに約11秒、パス2終了までに約29秒

(2)GAME9

 ・インタプリタでは、パス1終了までに4分11秒、パス2終了までに9分27秒

 ・コンパイラでは、パス1終了までに約11秒、パス2終了までに約25秒

という結果でしたので、インタプリタとコンパイラの実行速度比はGAME3でおよそ22倍、GAME9でおよそ23倍となりました。

作成したGAME9インタプリタとコンパイラをGAME3のそれと一緒にOneDriveに上げておきます。(GAME3のコンパイラについては、以前のブログで公開する際に作者の了解を得てあります。)


4 件のコメント:

  1. 懐かしいGAMEの話題、楽しませていただきました。
    私も6809のSBCを組んでみようかなぁ

    返信削除
    返信
    1. コメントをありがとうございます。
      大昔お世話になったGAMEですが、今でもSBC上でちょっとしたツールを作る際には役立っています。
      ぜひSBCを製作して使ってみてください。

      削除
  2. いきなりの長文失礼します。
    game9インタープリタをSBC6809用に書換えた際に見つけた問題点を2点ほど連絡させていただきます。
    その1 スタックの扱いについて
    6800版
    0049 0115 8E 00FF MAIN LDS #STACK1
    0196 0204 BE 0116 EDEND LDS MAIN+1
    0645 04BF BE 0116 ELSE LDS MAIN+1

    6809版
    0047 011B 10CE 00FF MAIN LDS #STACK1
    0188 0218 10FE 011C EDEND LDS MAIN+1
    0628 04F6 10FE 011C ELSE LDS MAIN+1

    6809ではLDSは2バイト命令になるので、EDENDとELSEラベルの「MAIN+1」は「MAIN+2」にしないと
    意図した$00FFがスタックSに格納されない。
    若しくは、スタックSではなくスタックUを使うようスタック関連の命令を変えると1バイト命令になるので
    オブジェクトのバイト位置を利用したテクニックには影響が出ないかも?(この辺は自分ではやってないので未検証)

    その2 変数の扱いについて
    6800版
    0403 034F 4F ADRVAR CLRA
    0404 0350 C4 3F ANDB #$3F
    0405 0352 CB 02 ADDB #2
    0406 0354 58 ASLB
    0407 0355 DF 70 AV1 STX $70
    0408 0357 7E 051A JMP TSTZR0

    6809版
    0390 0372 4F ADRVAR CLRA
    0391 0373 C4 3F ANDB #$3F
    0392 0375 CB 02 ADDB #2
    0393 0377 58 ASLB
    0394 0378 9F 70 AV1 STX $70
    0395 037A 16 01D9 LBRA TSTZR0

    コレについての説明は別の方のページに的確な説明があったので引用させてもらって、
    ttps://www.zukeran.org/shin/d/2024/06/04/bm-development-environment-8/より引用
    実は、GAMEでは、記号が特殊変数として利用できる。上の表の & ' * = がそれだ。先ほどのADRVARサブルーチンでは、これらの特殊変数のアドレスも同じ式で計算していた。

    例えば、 & ($26) なら、AND #$3Fしても$26、2を足して2倍すると$50。表の通りである。ここに対してダイレクトアドレッシングでアクセスしていたので、その部分も変更しないと動作しない。

    結局、$41-$5F,$20-$3F までの連続する$3F個(63個)の2バイト変数領域が必要で、これだけで126バイトを使ってしまう。これをGAME-MBのように全部0ページに残すかどうかの判断が難しいが、今回は全部別ページにしてみた。特殊変数だけ0ページに残すのが良いのかもしれないが、後で考えよう。
    ----------引用 終わり。
    引用元の「ず」さんの対処法については記述が見当たらないので不明ですが、
    私自身は変数の表記をすべて強制的に大文字に内部変換することで対処しました。使用可能変数が減るのは勿体ない気がしますが
    26個の変数だけでもかなりの事が出来ていたので、コレでヨシとします。

    私の動かしてる環境では、0ページはBASIC09のワークに、更にモニターも0ページを使っていたのを無理くり移動させての後に
    GAME9用に$06,$07辺りにワークを移動させたりしてるのでその辺の弊害も有るのでアセンブラソースは無茶苦茶なモノで…。

    返信削除
    返信
    1. 問題点を詳細な説明と共にご指摘していただきましてありがとうございます。最近はGAMEをほとんど使用していなかったので気付いていませんでした。
      これから問題点を確認して対処法を考えますので、解決できましたら改めて報告させていただきます。よろしくお願いいたします。

      削除