Making「ワイアノ」その2

Home PC-6001mkII Program etc

 このページでは、作成開始から画面表示までの過程を書いています。

Making「ワイアノ」 Top


2008/03/0x:きっかけ
 気が付けば、ここしばらく小さい更新しかしてないような気がする。P6月間まで3ヶ月弱という事もあるので、ここらで一つP6のプログラムを作ろうか。といえど、これといってネタはない。いや、正確に言うと「簡単にできそうな」ネタはこれといってない。

 この時点で、一つ試してみたいネタがあった。それは、2007年のHSPプログラムコンテストで受賞した「Paper Calc」だ。

 もちろん、HSP版のような表示を再現できるとは思っていない。再現したいのは、その中でも肝になる「動き」の部分だ。動きだけなら、ワイアーフレームのような表示にすればなんとかなるのではないか。

 描画速度が出なければ、いきなりお蔵入りになってしまう危うさを持っているが、とりあえず直線描画のプログラムを試作してみて様子を見よう。

〜2008/03/0x:まずは直線
 なにはともあれ、まずは直線だ。

 開始X,Y座標と終了X,Y座標のデータはそれぞれ2バイトで構成し、上位バイトを整数部、下位バイトを少数部分とする。これは、以前「回転」のプログラムを作成した時に割とうまくいったので、このデータ形式を踏襲する。

 次に、縦方向に長い直線と横方向に長い直線で描画処理を分ける。
 直線描画といっても実際は点の描画の集まりなのだが、この時例えば手で直線になるように点を打つとほとんどの場合(多分)以下の2パターンに大きく書き方が分かれる。

画面
 ここからわかることは、以下の事だ。
・縦方向に長い直線を描くときは、同じY座標には1つずつ点を描く。
・横方向に長い直線を描くときは、同じX座標には1つずつ点を描く。

 描き方が違うのなら、処理を分けたほうが単純化できるし、高速化もやりやすくなるだろうという考え方で突き進んでいく。

2008/03/08:線が描けた
 (中略)という事で、ようやく出来た。速度を見るために、縦長用とほぼ横長用で合計512本表示させてみる。
 画面
 この時点では5秒弱かかっていた。これが速いか遅いかが今ひとつ微妙だが、今のうちにもう少し高速化したほうがいいだろうか。

 ...微妙に線の終点の位置がずれているが、この辺りはあとで微調整しよう。

2008/03/09〜10:直線処理の高速化
 次の段階に行く前に、ここまでの処理を高速化することにする。対象がマシン語でマニアックだし、処理内容も個別処理で説明もしにくいので、ここでは一部だけかいつまんで紹介しておく。

 まずは、Z80命令の置き換えで、ほんの少し速くなる部分を修正する。
項目 使用前 使用後 削減ステート数
レジスタAの符号反転 XOR 0FFH
INC A
NEG3
レジスタDのビット移動 LD A, D
RRCA
LD D, A
RRC D4

 次に各Z80命令の挙動とにらめっこして、処理を変更していく。例えば以下のような感じだ。
項目 使用前 使用後 削減ステート数
C=20Hの時はB=00H,
C=0E0Hの時はB=0FFH
にする処理

・レジスタC=20H or 0FFH
LD A, C
RLCA
LD A, 00H

SBC A, A ;A:A-A-Cフラグ
LD B, A
LD A, C
RLCA
;Aの値は次のSBC A,Aの
;結果に影響なしなので省略

SBC A, A ;A:A-A-Cフラグ
LD B, A
7
レジスタL/レジスタE の
計算結果をAに返す処理

・レジスタE≧レジスタL
・レジスタH=00H
・レジスタD=00H
LD B, 08H
L011:
  RLCA
  ADD HL, HL
  SBC HL, DE
  JR NC, P011:
   DEC A
   ADD HL, DE
P011:
  INC A

 DJNZ L011:
LD B, 08H
L011:
  ;ADC命令で代用のため省略
  ADD HL, HL ;Cフラグ:OFF
  SBC HL, DE
  JR NC, P011:

   ADD HL, DE ;CフラグON
P011:
  CCF ;Cフラグ反転
  ADC A, A ;A:A+A+Cフラグ
 DJNZ L011:
32

 その他、実ははこれが一番効果のあった部分だったのだが、JP命令とJR命令をうまく使い分けると、場所によっては処理時間が短くなる。

 一般的な話でいうと相対アドレスジャンプの「JR」より絶対アドレスジャンプの「JP」が速いのだが、条件付ジャンプの場合はこの関係が逆転する場合がある。
「JR」は、ジャンプするときは12ステート,しないときは7ステートなのだが、「JP」はジャンプする/しないにかかわらず10ステートとなる(...この辺の理屈がいまだにわからないのだが)。

 ジャンプする確率が半分以下になったあたりから「JR」が「JP」よりも平均的に速くなる計算となるため、プログラム内でもジャンプする確率が半分以下の場合は「JR」,半分以上は「JP」に変更する。


 このようにして、こまごまとした部分を削減していった結果、ようやく1割ほど速くなった。
 ここから、しばらく小休止に入ってしまった...。

2008/03/26:再開
 ながらく放りっぱなしになっていたが、直線描画だけで終わらせるわけには行かないので、マシン語作成を再開する。

 まず、「Paper Calc」と同様に下図のように赤い点の部分に座標のデータを持たせる。この赤い点が、各パーツの基準点を表している。一方、格子の描画に必要な点(図では青い点)の座標は2x2パーツの範囲で基準点X,Y座標平均値で求めている。

 このため、まずは4x4の格子を描く目的で6x6の基準点を定義する。
画面
 とりあえず描画部分は後日行うとして、時間によって元に戻る処理と引きずりの伝達処理を作成する。戻り処理は、基準点の座標初期値との差分の1/8+差分符号分(-1〜1)だけ、X,Y,Z座標のそれぞれを戻している。

 引きずりの伝達処理は、各基準点の左右または上下にある基準点とのZ座標(凹み具合)を比較し、Z座標の小さい側が他方の座標に少し(X,Y,Zの各座標の差分の1/4)移動する。

 処理の都合上、戻りや移動の割合を1/8, 1/4といった2の階乗の値にした事で「Paper Calc」と若干異なる動きになると思われるが、肝心の動きは許容範囲になるのだろうか?

2008/03/29〜30:もったり
 3/27, 3/28に書き溜めていた、描画用座標の算出と格子描画の処理を入力する。最低限の実行部分が出来たので実行してみた。

 ...ちょっともったりしている。正確に測ったわけではないが、1秒間に6コマ程度だろうか。この程度の描画速度だと動きがぎこちない。もう少し速くならないだろうか?

 という事で、以下のように地道に修正を加えていく。
  • 格子の線の長さを短くする(32→24、線の描画部分は1.33倍の速さになるはず)
  • HL - DEの計算が多いので、あらかじめDEの値の符号を変えてHL + DEの計算に直す(16ビット演算では、引き算より足し算の方が4ステート分少ない)
  • 表レジスタを一時退避させ、裏レジスタを使っている部分を表レジスタに移動(表←→裏 切り替え分8ステートが稼げる)
  • よく見ると不要な命令を削除する。(PUSH/POPによる値の保護が必要でない前提だが、21ステートが稼げる)
 その他、画面消去の処理を加える。スピード重視のため、消去部分は最低限(97x97ドット分)をカバーする範囲のみ行い、さらにスタック用アドレスを一時的に描画位置に変更してPUSH文による描画を行う。理論上、この方法が最速のはずだ。

 ここまでで、表示のチェックを行う。が、なぜか画面消去時に一部ゴミデータが表示される。
しばらくして、ようやく原因がわかった。割り込み処理だ。割り込み処理では、メインの処理に影響を与えないように一時的にレジスタ値の退避(PUSH/POP)を行うが、この退避先アドレスを描画先に変更しているものだから画面にゴミのように表示されるらしい。

 という事で、画面消去を行っている間は割り込みの禁止を行うことにする。

 一応描画速度としてはぎりぎり許容範囲(秒間8〜9コマくらい)になったが、ここからキー操作の処理を加えるとなると速度的に心許ない。...格子の数を減らすか?


Making「ワイアノ」 その2