コマンド入力
目次
(DE)によるロード
2014.8.24午前追記
非常に小さいけど、重要な「無駄」の指摘がありました。
基本プログラムの LOOP2 ~HIT 部分の変更なので、該当箇所のみ示します。
LOOP2:
LD A,(DE) ; 7 (8)
CP (HL) ; 7 (8)
JR NZ,BAD ; 12 / 7 (13 / 8)
INC L ; 4 (5)
INC E ; 4 (5)
DJNZ LOOP2 ; 13 / 8 (14 / 9)
HIT:
元のプログラムでは、(HL) から A にロードして、(DE) の内容と比較して…という意図になっていました。
ここで、僕の勘違いで「比較はレジスタ間でしかできない」と思っていたため、C レジスタにロードしてから比較しているうえ、C へのロードは HL からしかできないため、HL と DE の交換命令が入っています。
現在は基本プログラムのバグは修正しています。
この無駄を指摘してくださった方は、最初に (DE) から A にロードしています。
通常、Z80 の 16bit レジスタをアドレスとしてアクセスを行う際には、(HL) しか使えません。HL 以外のレジスタをアドレスレジスタと見做すことはできないのです。
しかし、A レジスタとのロード命令だけは特別で、(BC) と (DE) も使えます。僕はつい、(HL) と思ってしまったのですが、ここで LD A,(DE) を使うことで、EX HL,DE は不要となります。
さらに、(HL) の表記は比較命令でも使えます。僕は一度レジスタに取ってから…と思っていたのですが、(HL) と比較するならレジスタへのロードは不要なのです。
これで、ループ内は 15 クロックも速くなり、一致時に 48クロック、不一致時に 61 クロックとなります。
43 +21*3 +61 +48+61 +48*6-5 +32*2 +21 = 644
総計は 644クロックです。
6502 基本
実は、6502 のプログラムを先に作り、Z80 基本のプログラムはそれを移植したものです。
データが 256 バイト境界にかからないようにする、などの仕様もそのため。
LDA #<COM1 ; 2
STA >1 ; 3
LDA #>PADHIST ; 2
STA >2 ; 3
LDA #<PADHIST ; 2
STA >3 ; 3
LDX #3 ; 2
LOOP:
LDA COMTABLE-1,X ; 4
STA >0 ; 3
LDY LENTABLE-1,X ; 4
LOOP2:
LDA (0),Y ; 5
CMP (2),Y ; 5
BNE BAD ; 3 / 2
DEY ; 2
BPL LOOP2 ; 3 / 2
HIT:
TXA ; 2
BNE END ; 3 / 2
BAD:
DEX ; 2
BNE LOOP 3 / 2
LDA #0 ; 2
END:
COMTABLE:
DB >COM3,>COM2,>COM1
LENTABLE:
DB 5, 3, 3
COM1:
DB $80,$01,$05,$04
COM2:
DB $40,$01,$04,$05
COM3:
DB $80,$40,$40,$40,$01,$01
ゼロページの >0 >1 に COM1~COM3 へのポインタが入ります。
また、>2 >3 に PADHIST へのポインタが入ります。
ループカウンタは、外側と内側でそれぞれ X と Y 。
ループカウンタ Y はインデックスとしても使われますが、減算していくため、Z80 版とコマンドの比較順序は逆になります。
公開時のプログラムにバグがありました。6502 で考えて Z80に移植したのだけど、文字列長を Z80 で扱いやすい形にしていたら 6502 ではバグになっていたという経緯。
指摘を受け、LENTABLE の値を文字列長 -1 にしてあります。速度は変化しません。
全体の前処理(LOOP まで)に 17クロック。
内部ループの前処理(LOOP~LOOP2)に 11クロック。
内部ループ内は、データバイトが不一致だと 13 クロックで、一致すると 17クロック。ただし、最後の1バイトの処理は1クロック速くなります。
後処理は、不一致時も、一致時も 5クロック。
Z80 と同じように繰り返し回数などを当てはめると…
17 +11*3 +13 +17+13 +17*6-1 +5*2 +5 = 209
総計は 209クロックです。
速度判定
6502 は 209 クロック。周波数を考慮して倍にすると、MSX での 418クロック相当の実時間になります。
Z80 は 644クロックなので、差は226クロック。ファミコンが35%ほど速いです。
問題の本質が「メモリ内を検索する」ことなので、メモリアクセスが遅い Z80 にはかなり不利な問題。
さらに、Z80 のレジスタに入りきらないほどのパラメータが必要になる…つもりで問題を設定したのですが、裏レジスタまで使えば入りました。
特殊な形の文字列検索なのでボイヤー・ムーア法の応用をすれば速くなるかもしれません。
といっても、例題では Z80 の例で、「1番目が1文字だけ一致して失敗したから、2番目は処理をスキップし、さらに3番目の1文字目は比較済みの状態から始める」と言うプログラムを組むのが関の山。プログラムが複雑になる割にどの程度高速化されるかは試してないのでわかりません。
パッド関連3題では、ちょっと Z80 不利な問題にしすぎた感じ…特に、コマンド入力は当時のゲームではあまり見られませんでしたし、遅いから性能が悪かった、と言うわけではないかな。
次の例題は…割り算