GAME68クロスコンパイラを書いてみた(2) (ベーシックマスター開発 その21)

BASICMASTER

前回記事で「区切りとして github に置きました。この版はこれで終わりにして、第2版を作ろう」と書いたのですが、2週間で書いた割には意外に実用になるので、バグ修正と高速化を1週間ほど頑張ってみました。

現在では、ASCII 1979年7月号P.26(エンサイクロペディア・アスキー Vol.4 P.213)の「GAME80コンパイラ」記事に掲載されている「スターファイヤー」ゲームも動くようになっています。

私にはインタプリタの速さでちょうど良いぐらいで、コンパイルすると速すぎてゲームになりません。記事にも書かれているように、コンパイラの速さを活かして、敵機の数を増やすなどの改造を行なった方が良いです。

スターファイヤーゲームの修正点

ASCII 1979年7月号の「スターファイヤー」はTK-80BS用なので、そのままでは動作しません。下記の修正を行なっています。

TK-80BSは画面横幅が32文字で、ベーシックマスターと同じなので移植はやりやすいです。
縦幅は16行で、ベーシックマスター(24行)よりも小さく、そのためスターファイヤーの画面も下1/3が空いています。この部分にゲーム画面を拡大するのも面白いと思います。


--- starfire.game.orig	2024-08-15 23:34:24
+++ starfire.game	2024-08-15 23:34:14
@@ -1,22 +1,23 @@
-10** STAR FIRE **
+10** STAR FIRE ** ASCII 1979.7 P.26 / Encyclopedia ASCII Vol.4 P.213
 20 E=0 G=0 H=0
 40 SCORE=0 FUEL=600
-50 V=$7E00 KEY=$7DFC P=$847D
-60 >=$FA6C
+49* V:VRAM TOP, KEY:TK-80BS key in port, P:cursor X,Y (BS:1 origin,BM:0 origin)
+50 V=$0100 KEY=$7DFC P=$000F
+60 >=$FFEF
 70 LOCK=0 TOPEDO=0
-80 P:1)=16
+80 P:1)=16-1
 90 X='30 Y='15-1
 100 B=Y*32+X-1+V
-110 R:0)=$A8 B:1)=$CA B:2)=$A7
+110 B:0)=$8A B:1)=$94 B:2)=$88
 120 ;=L>0 L=L+1 #=200
-130 ;=(X=15)*(Y=7) L=1 P:0)=1 "LOCKED!!" #=200
+130 ;=(X=15)*(Y=7) L=1 P:0)=1-1 "LOCKED!!" #=200
 140 X=X+'3-2 Y=Y+'3-2
-200 P:0)=12
+200 P:0)=12-1
 210 "SCORE:" ?(4)=S
 220 " FUEL:" ?(4)=F
 240 ;=F<1 #=5000
 250 F=F-1
-300 A=K:0) ;=L #=350
+300 A=[ ;=L #=350
 310 ;=A="I" Y=Y+1
 320 ;=A="J" X=X+1
 330 ;=A="K" X=X-1
@@ -25,7 +26,7 @@
 360 ;=(A="F")*T #=4000
 400 ;=L<4 #=500
 410 L=0 X=X+'3-2 Y=Y+'3-2
-420 P:0)=1 "        "
+420 P:0)=1-1 "        "
 500 ;=L !=3000
 510 ;=L=0 !=3050
 520 C=(+(X-15)<5)*(+(Y-7)<4)
@@ -36,32 +37,32 @@
 960 ;=(Y<0)+(Y>14) H=H+1 #=90
 990 #=100
 3000** LOCK TARGET**
-3010 N=$8B M=$9A !=3100
+3010 N=$86 M=$85 !=3100
 3020 N=$20 M=$20 #=3100
 3050** TARGET SCOPE **
-3060 N=$A4 M=$A4
+3060 N=$80 M=$80
 3100 I=143,207 V:I)=N @=I+32
 3110 I=271,335 V:I)=N @=I+32
 3120 I=234,237 V:I)=M @=I+1
 3130 I=241,244 V:I)=M @=I+1
 3140 ]
 3200** TARGET INDICATER **
-3210 N=$A7 M=$A5 !=3300
-3220 V:Y*32+21)=$BC
-3230 V:X+352)=$BD
+3210 N=$88 M=$8B !=3300
+3220 V:Y*32+21)=$7B
+3230 V:X+352)=$7E
 3240 ]
 3250** OFF INDICATER **
 3260 N=$20 M=$20
 3300 I=149,341 V:I)=N @=I+32
 3310 I=362,372 V:I)=M @=I+1
 3320 ]
-4000 T=0 N=$B3 M=$B6 !=4300
+4000 T=0 N=$FC M=$FD !=4300
 4010 ;=V:239)=" " #=4200
-4100 V:206)=$FA V:207)=$FA
-4110 V:238)=$FA V:239)=$FA
-4120 V:240)=$FA V:270)=$FA
-4130 V:272)=$FA
-4150 P:0)=1 "GOT HIM!!"
+4100 V:206)=$9D V:207)=$9D
+4110 V:238)=$9D V:239)=$9D
+4120 V:240)=$9D V:270)=$9D
+4130 V:272)=$9D
+4150 P:0)=1-1 "GOT HIM!!"
 4160 S=S+50
 4170 I=1,1000 @=I+1
 4180 G=G+1
@@ -73,7 +74,7 @@
 4310 @ V:I)=N V:J)=M
 4320 I=I-31 J=J-33 @=(I=239)
 4330 ]
-5000 P(0)=$508
+5000 P(0)=$508-$101 >=$FFEF
 5010 "*** GAME OVER ***"//
 5020 "GET SHOT:   " ?=G /
 5030 "MISS SHOT:  " ?=E /

コンパイラの改善点

  • “…” と / の連続を許すようにした。小手先の誤魔化しなので、救えないパターンもありそう。
  • 負数の除算、MODの最適化が誤っていた。インタプリタでは、負数の除算は正数同士の演算結果に符号をつけるようになっていたので、それに合わせた。遅くなった。MODも同様で、剰余は正の値になるようにした。
  • 乗除算・乱数を高速なルーチンに差し替え
  • その他、バグ修正・姑息な高速化


資料

コメント

  1. にこほん より:

    機能のコメント、あげた直後のプレヴューで一部端折られてるなと思ったら
    やはり一部抜けがあるようですので補足です。
    ——————————————————————————–
    optimize.cファイルの
    関数 void optimize_do_loop(){ 内の

    if(node_u->kind==ND_SETVAR
    && strcmp(node_u->str,odl[n].var)==0){ // 制御変数への代入がある
    odl[n].opt = 0; //ofl[n].opt = 0;
    // printf(“; break control var ‘%s’:”,odl[n].var);

    ——————————————————————————–

    これでも抜けるようなら、私の方のブログにて全文まとめて出してますので参照願います。
    二度手間、三度手間 申し訳ありません。

    • ず@沖縄 より:

      ブログも参照し、修正箇所は把握いたしました。

      テストして、修正を反映する予定です。

      ありがとうございました。

    • ず@沖縄 より:

      blogspotへのコメント投稿がうまくいかないので、こちらで書きます。
      —-
      「変数Sとそれを負数にした-Sが同一の値だと認識されて」ですが、codegen.c の void LDAr_I(char *r,int v) にバグがあります。定数をロードしたときに、レジスタと変数の紐付けを消すべきですが、その処理が抜けていました。以下で治ると思います。

      @@ -1136,6 +1136,11 @@ void LDAr_I(char *r,int v)
      // どこにもないのでLDArする
      printf(“\tLDA%s\t#%d\n”,r,v); // 2 2
      }
      + lv_free_reg(r);
      + if (strcmp(r,”A”)==0
      + || strcmp(r,”B”)==0) {
      + lv_free_reg_D();
      + }
      lv_add_reg_const(r,v); // レジスタの値をアップデート
      if(pexist){ // ペアの値がわかってるなら、Dも設定できる
      if(strcmp(r,”A”)==0){

  2. にこほん より:

    codegen.cファイルの

    関数 void epilogue(){ 内の

    for(int i=1; i<odl_n+1; i++){
    if(odl[i].opt==0){ //if(ofl[i].opt==0){
    continue;
    }
    for(int j=0; jkind==ND_SETVAR
    && strcmp(node_u->str,odl[n].var)==0){ // 制御変数への代入がある
    odl[n].opt = 0; //ofl[n].opt = 0;
    // printf(“; break control var ‘%s’:”,odl[n].var);

    ——————————————————————————–

    分かりづらい提示で申し訳ないのですが、
    コメント「//ofl[n].opt 」が含まれる行が修正箇所です。
    アセンブラソース出力の終わりの方に出てくる

    V_F_DZ2_8 RMB 2 ; for loop pseudo array
    V_D_BI1_2 RMB 2 ; do loop pseudo array

    for文とdo文の為の疑似関数のバッファ確保文の オリジナルで言うところの「_D_BI1_」の名を冠した一行が
    修正しないと出力されない症状があって見つけました。
    for文の方は出ていたので、for文の出力に影響を与えている変数などと対になるdo文側の変数を追いかけて、
    私が使っているGeanyエディタだと同一フォルダ内のファイルに対して指定したキーワードが使われている箇所を
    検索で弾き出す機能があるのでそれで見つけました。

    オリジナルのソースから、削除したり前後の位置を変えたりしているので
    diffを取っても参考にならない気がするので申し訳ないです。

    ただ、これもバイナリ実行形式じゃなく アセンブラのソースで出す形だから見つけられたのが
    大きな利点だと思います。

  3. ず@沖縄 より:

    使っていただけて大変嬉しいです。ありがとうございます。

    GAME-CCは6800用に最適化しているんで、6809だと素直にコード生成した方がマシな部分が多数あると思います。

    AccA:Bでアドレス生成してXに移動する処理が重たいので、なるべく省略するようにしているのですが、6809だと素直に演算した方が早い部分が多数あるはずです。

    変数の間接参照も、アドレッシングモードに落とし込んだ方がいいはず。

    ところで、記事の中で、

    > DO-untilループの出現数カウンタのodlの扱いが、for-nextループの出現カウンタのoflと
    > 混同されているところを2箇所発見修正

    とあったのですが、元ソースの誤りであれば教えていただけると助かります。
    よろしくお願いします。

  4. にこほん より:

    GAME-CCを6809用に改変してみました。
    ワンボードマイコン用に最低限の機能に絞った形になるので、BM-L2の機能の大半は削除しましたが、ソースも付けた形での公開ですので6809搭載機なら移植は簡単なものだと思います。
    「ず」さんの アセンブラソースの形で出す やり方をしてくれるコンパイラを探していたので非常に有り難い限りです。今回MC6800のアセンブラのソースをじっくり読むのが初めてだったのですが、6809の先祖だけあって非常に読みやすいソースで助かりました。また適宜 日本語のコメントを随所に入れていただいていたのも移植の助けになりました。

タイトルとURLをコピーしました