OPASによるプログラミング!

BASICで多階調表示をしよう


 今回はBASICで多階調表示を行う方法について書きます。
 知ってのとおり、ポケコンは白黒2階調しか表示することができません。これを多階調表示を行うわけですが、そもそもハードウェアが多階調表示に対応していないのです。
 しかし、実は「表示だけ」なら、いとも簡単に多階調が実現できるのです。 例えば赤い物体と青い物体を高速に交互に表示した場合、紫になりますよね。もう、この時点で原理が分かってい人は多数いると思いますが、それだけのことなのです。

 では、次のプログラムを入力してください。

10 CLS
20 LOCATE 0,0:PRINT "* *"
30 LOCATE 0,0:PRINT "** "
40 GOTO 20

 実行すれば分かりますが、画面に3つの「*(アスタリスク)」が表示されます。一番右の「*」は普通に表示されていますが、残りの2つは薄く(灰色に)表示されていますね。

 このように2つのグラフィック画面を用意して、2つを交互に表示してやることで中間調の表示が(疑似的に)実現できるのです。2つのグラフィック画面をそれぞれ画面A、画面Bとすると次のようになります。
画面A、画面Bともに点灯しているドット ・・・・ 黒
画面A、画面Bともに点灯していないドット・・・・ 白
画面A、画面Bどちらか点灯しているドット・・・・ 灰色
 では、先程のプログラムに次のリストをつけ加えてみてください。

 35 FOR I=1 TO 7:NEXT 

 今度は、3つの「*」の中で、先程は同じ濃さだった真ん中と右の「*」の濃さが違うことに気が付くでしょう。画面B(30行)の「*」は画面Aの2倍表示しているのでその分だけ濃くなっているのです。
画面A のみ点灯しているドット ・・・ 薄い灰色
画面B のみ点灯しているドット ・・・ 濃い灰色

※画面Aのみ点灯しているドットは全体の3分の1の時間点灯していて、画面Bのみ点灯しているドットは全体の3分の2の時間点灯しているためである。当然のことながら両方とも点灯しているドットは常に点灯しているので黒のままである。

 これにより4階調を実現するための原理はお分かりでしょうか?
同様に画面Bの2倍の時間表示する画面Cを用意すれば8階調表示ができ、さらに、その2倍の時間を表示する画面Dを用意すれば16階調も実現できるのです!

 ところが、これには問題があるのです。それは「高速に表示しなければならない」ということです。
 では、どの程度の速度にすればよいかということですが、60コマ/秒(1秒間に60回表示を切り替える)を実現できれば言うことはありません。60コマ/秒というのは最近のアーケードゲームでよく使われていますが、ゲームをやらない人のために別のものに例えると蛍光灯と同じ(ただし中部以東は1秒あたり50回)なのです。蛍光灯はちらついて見えませんよね。
 はっきり言って60コマ/秒を実現するためには画面Aを1秒あたり180回表示するだけの速度が要求されるのです。これは言い換えれば1回あたりの表示時間が約5.5m秒なのですが、これは理論上不可能です。PRINT文の実行速度は「PRINT文の高速化」にも掲載していますが、あの数値にLOCATEの2.3m秒をプラスしなくてはいけないのです。となると、やはり、適当なところで妥協するしかありません。幸いポケコンのLCDは反応があまりよくありません(笑)ので、20コマ/秒なら十分、10コマ/秒程度でもそれほどちらつきを感じずに済むと思います(たぶんね・・・)。そうなると、画面Aは1秒あたり30回の表示速度(33m秒)で済むために何とか実現できそうです。

 では、きちんと多階調表示ができるのか実際のプログラムで見てみましょう。まず、RUN*AREAとしてデータ用のエリア確保をしてください。とりあえず、24×24ドットのグラフィックで4階調表示を行いますが、データを作るのが面倒な人はRUN*SETとしてください。自分でデータを作りたい人はBFAE6hから72バイト(24ドット×3ライン)が、画面Aで、その続きから72バイトが画面Bとなりますので適当なグラフィックをPOKE文で書き込んでください。

リスト1
10 CLS
20 *MAIN
30 POKE &BFC93,&80,&FA,&B
40 GOSUB *SUB
50 END
100 *SUB
110 LOCATE 0,0:PRINT "アイウエ"
120 LOCATE 0,0:PRINT "オカキク"
130 LOCATE 0,0:PRINT "ケコサシ"
140 LOCATE 0,0:PRINT "スセソタ"
150 LOCATE 0,0:PRINT "チツテト"
160 LOCATE 0,0:PRINT "ナニヌネ"
170 FOR I=1 TO 25:NEXT
180 IF INKEY$=" " RETURN
190 GOTO *SUB
200 *SET A=255:FOR I=0 TO 11:B=-(I>5)*A:C=A-B:POKE &BFAE6+I*12,A,A,A,B,B,B,C,C,C,0,0,0:NEXT:GOTO 10
210 *AREA POKE &BFE03,&1A,&FD,&B,&80,1,0:CALL &FFFD8

 なお、このプログラムを実行すると多少画面がちらつくものの疑似的に4階調表示が行われていることが分かる(見えない場合はコントラストを調節してね)と思います。なぜ、ちらつくかというと、1秒間に7回程度しか書き換えを行ってないからです。これは先程のボーダーライン(1秒当たり10回)の3分の2の速度でしかありません。

 このプログラムは速度よりも原理の分かり易さを優先して作られています。110〜130行が画面A、140〜160行が画面Bに相当します。1070行のFOR文で画面Bが画面Aの2倍の時間表示するように調節しています(実は計算したあときちんと4階調表示に見えるようウエイトは微調整している)。
 コラム FOR〜NEXTの謎

 実は、FOR〜NEXTの厳密な実行時間を計算で求めるのはほぼ不可能なのである。なぜなら、同じループ数でも開始値が変われば実行時間がわずかに変わるのである。しかし、もっと厄介な問題もあるのだ。
 何と99回ループよりも100回ループの方が速いのである。このようにループ数が増えたら逆に速くなるようではどう考えても単純計算で求めることは無理なのは明白だろう。


 では、リスト1を高速化しましょう。
 なお、下記のリスト2ではの110行目に直接入力できない制御文字が使用されています。これを入力するには以下のようにしてください。
  1. RUNモードにする
  2. A$=CHR$29+CHR$29+CHR$29+CHR$29 [RETURN]  
  3. B$=CHR$31+A$ [RETURN]
  4. C$=B$+B$+CHR$30+CHR$30+A$+B$+B$ [RETURN]   
  5. PROGRAMモードにする
  6. C$ [RETURN] [+/-]
  7. 行番号その他を挿入する


リスト2
10 CLS :OPEN "SCRN:" FOR OUTPUT AS #1
20 *MAIN
30 POKE &BFC93,&80,&FA,&B
40 GOSUB *SUB
50 END
100 *SUB
110 LOCATE 0,0:PRINT #1,"アイウエ↓←←←←オカキク↓←←←←ケコサシ↑↑←←←←スセソタ↓←←←←チツテト↓←←←←ナニヌネ"
120 IF INKEY$=" " RETURN ELSE *SUB

 実行してもらえたら分かりますが、何とリスト1の3倍の速度を実現しています。ここで、気付いている人もいるかもしれませんが、リスト2の方には速度を調節するFOR文がありません。なぜかというと、リスト2では画 面Aを実質14m秒(ボーダーラインの2倍速!)で表示しているために、LOCATE、PRINT#、IFで十分なウエイトが得られるからです。

 ここまで読んで分かるようにこの多階調表示(もどき?)はOPASを使用しなくてもフォント書き換え(+PRINT文高速化&制御文字使用)で実現できます。なぜ、仕様上OPASに入っているかというと、64文字制限のためです。グラフィックを複数枚用意しなくてはいけないのでデータ量が多くなりますからね。
 でも、私が言うのも何ですが、はっきり言ってこのルーチンは使い勝手がよくありません(笑)。というのも、表示するリスト1、リスト2を比べてみて分かるように表示速度によってウエイトを調節しなくてはいけないからです。画面Aを表示するのにかかっている時間を実測で求めるなり、計算で出すなりしなくてはいけません。さらに見た目で微調整も必要です。これは、非常に大変なことです。
 でも、それほどこのルーチンを使用しない(同じ表示エリアのグラフィックしか表示しない)のであれば、それほど問題にはならないとは思います。ちらつきを許容できる最大サイズは4階調で48×32ドット、8階調で24×24ドットです。正直言って、タイトル画面かイベントのグラフィック程度にしか使用するのは難しい(濃さを保つため常に一定のウエイトを加えないといけないため)常にですが、ぜひ一度使ってみてください。とはいうものの私は使ったことがありませんが(笑)


さらにOPASを活用したい人へ→OPAS応用編


RETURN

inserted by FC2 system