2009年2月7日土曜日

TinyでPONG!(ソフトウェア3)

TinyでPONG!(ソフトウェア2)では映像出力の最前線の部分の説明をしました。1水平ライン分の映像データは24個の汎用レジスタのR0~R23に用意されているということが前提でした。逆にいえばデータをレジスタに入れさえすればいいわけです。

全速力で1ラインの映像を出力した後は、次のライン出力の出番が来るまでのつかの間にデータを仕込みます。ソースは"draw.asm"にあります。最初に判定すべきは何のデータを仕込まなければならないのかです。つまり次に何行目を走るのかで書きだすものを変える。大くくりでは、
  • 1~10行目 → サイドライン(横一本ずっと白=「1」)
  • 11~226行目 → コート内部(センターライン、スコア、ボール、パドル)
  • 227~236行目 → サイドライン
となります。この判定をしているのが以下の部分。
  1.         ;; --- prepare next raster ---  
  2.         cpi     nxline, COURT_MIN; +1=2  
  3.         brlo    sideline        ; +1=3  
  4.         cpi     nxline, COURT_MAX; +1=4  
  5.         brlo    field           ; +2=6  
  6.   
  7.         ;; --- draw sidelines ---  
  8. sideline:                       ; draw sideline  
  9.         filrast                 ; (+24)  
  10.         rjmp    eol  

次に何行目を走るのかはnxline(ここではR27と同義)に入っています。それがコート内部にあるかどうかの比較があって、外部であればサイドラインを書くべくfilrastを実行して終わりです。filrastはマクロで、汎用レジスタR0~R23の全ビットを1に立てるだけです。これは簡単。

コート内部だったら前出の通りサイドラインとボールとスコアとパドルのデータを用意しなくてはなりません。これらは排他的ではなくて、すべて書かなければならない状態もあり得ますから1つ書いてサヨナラというわけにはいきません。全部説明するのも冗長なのでここではまずボールを書く部分を。
  1.         ;; --- draw ball ---  
  2.         .def    pos     =r24  
  3. drawball:  
  4.         lds     pos, Ballt      ; +2=10  
  5.         cp      nxline, pos     ; +1=11 skip if (line > Ball_top)  
  6.         brlo    drawpad         ; +1=12  
  7.         addi    pos, BAL_HEI    ; +1=13  
  8.         cp      pos, nxline     ; +1=14 skip if (line < Ball_bot)  
  9.         brlo    drawpad         ; +1=15 / +2=16  
  10.   
  11.         .def    pattern =r26  
  12.         clr     zh              ; +1=16  
  13.         lds     zl, Ballad      ; +2=18  
  14.         lds     pattern, Ballpt ; +2=20  
  15.         st      z, pattern      ; +2=22  
  16.         com     pattern         ; +1=23  
  17.         std     z+1, pattern    ; +2=25 (r24 can be written)  

次の行nxlineがボールの上辺の位置Balltと下辺の位置Ballt+BAL_HEIの間にあるかどうかの判定が冒頭にあります。なければそこにはボールがないので書く必要がないということになり、次のパドルを書きに行きます。

ボールを書く場合、ボールのバイトアドレスBalladでレジスタ番号を指定し、ボールのビットパターンBallptをそのレジスタに書き込みます。ここがAVRのアーキテクチャの特徴をうまく使った部分で、レジスタがメモリアドレスのゼロ側にマップされていることを利用しています。つまり、メモリアドレス0x0000はR0を、0x001fはR31を指し示し、この例のようにZレジスタ間接などでもあたかもSRAMをアクセスしているかのごとしです。このようにできることはデータシートには書いてなくて(探しが足りなかったのかも)、実験してみたらできてしまってラッキーでした。こいつは便利だ。AVRすごい!

ボールのビットパターンを書くと言いましたが、ボールの幅が8ドット=8ビット、レジスタが8ビット単位なので(ほとんどの場合)2つのレジスタにまたがって書かなければなりません。この例ではまず指定されたレジスタに左側を書き、次のレジスタに右側を書いています。右側のパターンはちょうど左側のパターンのビット反転になっているのが分かりますでしょうか。(だからcom命令で反転しています)

ボールの形を書く


今回はこのくらいで。次はスコアの表示です。

そうそう、ソースアーカイブにレジスタマップ(regmap.txt)を入れておいたのを忘れていました。今Excelで開いてもう少し見やすくしたイメージを下に貼っておきます。

レジスタマップ

0 件のコメント: