2009年2月20日金曜日

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

水平1ライン分を描画するプログラムの仕上げとしてスコア表示を説明しましょう。

スコアは2桁の数字です。普通に考えれば1桁分の「数字フォント」を0から9まで10種類用意することになるのですが、ここではフォントの幅=ドット数も勘案して2桁で1つの数字として扱っています。2桁だと100種類のバリエーションができてフォントの格納領域が膨大になってしまいそうですが、そこはPONG。0点から15点まで16種類しか必要としません。その定義が以下の"fconst.asm"です。

fconst.asm ("1"を強調表示しています)

.setと.dbがあって一瞬なんだこりゃ?ですが、フォントをビジュアルで定義するための小技です。1のビットが白――文字部分――になります。1つの数字は横×縦=4×5ドットの枠の中に3×5ドットとして表現されています。これを横に2桁分並べて8×5ドットの「2桁数字」を1フォントにしています。

一方、画面デザイン上の問題として、フォントの1ドット=画面上の1ドットにしてしまうと数字があまりにも小さくなりすぎてバランスが良くありません。数字をある程度の大きさで見せるためにはサイズを拡大してあげないといけない。そこでフォントの1ドット=画面上の4ドットと縦横4倍になるようにしています。

また、スコアの数値自体はSRAM上の変数ScoAとScoBに入っているのですが、そこから上記のフォントデータに変換して、4倍にふくらませて…とやっていると水平ブランキング期間内に処理が終わりません。そこで、スコア→フォントデータへの変換部分だけはあらかじめ――垂直ブランキング期間内に――終わらせておくという措置をとります。その処理をするのが"font_cache.asm"です。ScoA/Bの値を表すフォントデータがSRAM上のバッファ変数Ftbufに格納されます。

さて、Ftbufに格納されたフォントデータを描画する部分が"draw.asm"の最後の部分です。まず、スコアが描かれる縦位置に来たかどうか判定します。

;; --- draw scores ---
drawscore:
mov zl, nxline ; +1=42 prepare for font load
subi nxline, DGT_TOP ; +1=43
cpi nxline, DGT_BOT+1-DGT_TOP; +1=44
brsh eol ; +1=45

スコアが描かれる位置に来ました。次はフォントを縦4倍に広げるのですが、どうしたらよいのでしょう。答えは「4回足踏みして待つ」です。次にどこの行を走査するのかを示す変数nextlineは1ずつ増加するのに対し、4回同じデータを描画すれば縦が4倍に伸びることになります。nxline(のコピーのZL)を4で割っている(LSR命令が2回)のはそのためです。

lsr zl ; +1=46
lsr zl ; +1=47
addi zl, low(Ftbuf)-DGT_TOP/4; +1=48
clr zh ; +1=49

そして横の4倍化。「11110000」と「00001111」の2つのビットパターンを用意しておいて、フォントデータの1になっているビットの位置によってこれら2つを使い分けて行のデータと論理和を取ります。分かりにくいと思うので実例で。

今、描画しようとしている先の行には下記のようにボールの一部が描画されているとします。R7-R10はスコアを表示する位置にあたります。


ここにたとえばフォントデータ"14"を重ねて描画しようとしたとして、
    11001010
    01001010
    01001110 ←この行を描画
    01000010
    11100010

今はそのフォントの3行目だとすると、その描画データは01001110です。これを上位ビットから判定していきます。ルールは0なら何もせず、1ならそれが偶数ビットであればパターン11110000を、奇数ビットであればパターン00001111を重ねるというものです。するとこうなります。
    ビット7は0 … 何もしない
    ビット6は1 … パターン00001111をR7に重ねる
    ビット5は0 … 何もしない
    ビット4は0 … 何もしない
    ビット3は1 … パターン11110000をR9に重ねる
    ビット2は1 … パターン00001111をR9に重ねる
    ビット1は1 … パターン11110000をR10に重ねる
    ビット0は0 … 何もしない

図示するとこうです。

この部分のソースはこれです。ScoA側だけ示します。SBRC命令を使って0のビットをスキップしています。ScoBについても同じ処理になります。

.def fontd =r24
.def pat10 =r27
.def pat01 =r26
ldi pat10, 0b11110000; +1=50
ldi pat01, 0b00001111; +1=51
;; for Score A
ld fontd, z ; +2=53 load a font row of A
sbrc fontd, 7 ; +1=54
or rast7, pat10 ; +1=55
sbrc fontd, 6 ; +1=56
or rast7, pat01 ; +1=57
sbrc fontd, 5 ; +1=58
or rast8, pat10 ; +1=59
sbrc fontd, 3 ; +1=60
or rast9, pat10 ; +1=61
sbrc fontd, 2 ; +1=62
or rast9, pat01 ; +1=63
sbrc fontd, 1 ; +1=64
or rast10, pat10 ; +1=65

以上で映像出力と描画関連のプログラムの説明は終わりです。ゲームの進行については垂直ブランキング期間――1/60秒に1回かつ1.28msの間だけ――に行われますが、それはgame.asmを読んでいただくことにしましょう。

長々と説明をしてきましたが、とりあえずソフトウェアの説明はこれで終了です。お疲れ様でした!

0 件のコメント: