ランダムアクセス
目次
テーブル参照&POPアクセス
2014.8.8 午前追記
6502 では最初からテーブル参照していたのですが、やはり Z80 でもテーブル参照するプログラムが投稿されました。
インデックスレジスタが使いにくいとはいえ、やっぱテーブル参照の方が速いですね。
LD IX,0 ; 20 (22)
ADD IX,SP ; 15 (17)
LD SP, DATA ; 10(11)
JP START ; 10(11)
LOOP:
; テーブルを参照して VRAM の縦位置を取得
LD A, (HL) ; 7 (8)
INC L ; 4 (5)
LD H, (HL) ; 7 (8)
; 横座標とキャラクターを得て書き込み
POP BC ; 10 (11)
OR C ; 4 (5)
LD L, A ; 4 (5)
LD (HL), B ; 7 (8)
START:
; 縦座標を得て終了条件チェック
POP HL ; 10 (11)
SLA L ; 8 (10)
JP NC, LOOP ; 10 (11)
END:
LD SP,IX ; 10 (12)
VADDR:
DW VRAM + (0 *32), VRAM + (1*32), VRAM + (2*32), ... , VRAM + (24*32)
このプログラム、作者さんは「反則っぽい」と明記していました。元プログラムでは、たしかに反則っぽい点が2つ。
プログラムの中でスタックポインタを変更して戻さないことと、DATA 内に用意されるデータが、用意する側に負担がかかる形式になっていたことです。
このうち、スタックポインタに関しては前処理と後処理で保護するようにしています。このため、投稿プログラムでは 3990 クロックで終了していたのが、少し増加しています。
DATA 内は、縦座標、ダミーデータ、横座標、キャラクタ、の順に並んでいます。
規定で「扱いやすくするために4バイト1組にしてもよい」ことを明記してあるので、ダミーデータが入っていても構いません。
このダミーデータが反則っぽい点の2つ目。ダミーだから何でもよいのではなく、固定の値を期待しています。具体的には、VADDR とラベルが付いているアドレスの上位バイトが入っていなくてはなりません。
どうしようかと思ったのですが、必ずダミーデータとして、固定位置に固定データが入っているのですから、「呼び出し元には負担をかけていない」と判断しました。データ書き込み時にいじらないように注意するだけですからね。
ループは START から始まります。
POP HL で、縦座標とダミーデータ(VADDR の上位バイト)を取り出します。HL 全体としては、VADDR テーブルへのポインタとなります。
データの取り出しは POP で行っているため、自動的に次のデータへポインタが移動します。当時は結構使われた高速化テクニックです。
VADDR テーブルの中身は 16bit なので、L を左シフトします。これが同時に、縦座標が 255 の時に終了する、というチェックにもなっています。
そして、HL を使ってテーブルを取り出します。テーブルの中身は、VRAM の縦座標アドレスをあらかじめ計算したものです。(48バイトのテーブルなので、それほど大きくはないです)
つづいて、POP BC で横座標とキャラクタを読み出します。これで必要なものはほぼそろったので、後は書き込むだけ。
スタックポインタの保全に、命令の遅い IX レジスタを使っているため、前後処理に時間がかかっています。
前処理が 61クロック、後処理が 12 クロック。
1キャラの書き込みに 82 クロック。終了条件の判定に 32クロックかかっています。
61 + 12 + 82*48 + 32 = 4041
総計で 4041 クロックです。
自己書き換え
2014.8.10 午前追記
SP 退避を自己書き換えで行うと速い、という投稿がありました。
「ROM 化できない」と投稿にあったのですが、大丈夫だと思いますよ。メモリの少ないファミコンですら、内部 RAM に転送してから呼び出すことで高速化する工夫とかあったそうですから。
メモリが最低でも 8K もある MSX なら全然 OK 。
前処理と後処理が変わるだけなので、その部分のみ示します。
LD (END+1),SP ; 20 (22);
LD SP,DATA ; 10 (11);
POP HL ; 10 (11);
SLA L ; 8 (10);
JR C,END ; 12 / 7 (13 / 8);
LOOP:
END:
LD SP,0 ; 10 (11)
自己書き換えにより、IX レジスタが不要になっています。IX レジスタは使うだけで遅いので、前処理で 17クロック、後処理で 1 クロックの高速化。後処理の影響が小さいのは、レジスタ参照からメモリ参照に変わったためです。
自己書き換えだけでなく、「START」(ループ末尾の少しの処理)へのジャンプも無くし、その部分を前処理の中で行っています。
終了条件判定は入っているけど、START へのジャンプよりもこれで12クロック速くなります。
合わせて 30 クロックの高速化。総計で 4011クロックです。