|
|
|
S59/02/19
|
阿閉雅宏の
|
Z-80まにあくす
|
Stage.02 |
|
解説:杉之原名人
|
|
|
|
|
|
近頃SPの変わった使い方がでてますが、今回は"ON~GOTO"にあたるプログラムを紹介しませう。リスト1が"ON A+1 GOTO ~"となるでしょうか。ただし、Aの最大値は$FFです。ジャンプアドレスはCALL命令以降に並んでいるものです。リスト2は"ON A GOTO ~"で、CALL命令のすぐ後にAの最大値を入れてください。その後にジャンプアドレスを入れておいてください。リロケータブルなプログラムです。これは一種のサブルーチンと思ってください。 |
|
リスト1 ON (A+1) GOTO |
|
8000 |
E3 |
EX |
(SP) , HL |
|
|
HL = リターン先アドレス(=ジャンプテーブル) |
8001 |
D5 |
PUSH |
DE |
|
|
DE退避 |
8002 |
5F |
LD |
E , A |
|
|
DE = オフセットNo. |
8003 |
16 00 |
LD |
D , 0 |
|
8005 |
19 |
ADD |
HL , DE |
|
|
HL = HL + オフセットNo. * 2 |
8006 |
19 |
ADD |
HL , DE |
|
8007 |
5E |
LD |
E , (HL) |
|
|
DE = 目的ジャンプ先アドレス |
8007 |
23 |
INC |
HL |
|
8009 |
56 |
LD |
D , (HL) |
|
800A |
EB |
EX |
DE , HL |
|
|
HL = 目的ジャンプ先アドレス |
800B |
D1 |
POP |
DE |
|
|
DE復元 |
800C |
E3 |
EX |
(SP) , HL |
|
|
スタックに目的ジャンプ先アドレスを乗せる |
800D |
C9 |
RET |
|
|
|
目的のジャンプ先へジャンプ |
|
|
|
|
|
|
|
9000 |
CD 00 80 |
CALL |
$8000 |
|
|
ON (A+1) GOTO ルーチンを呼び出す |
9003 |
00 91 00 92 |
DEFW |
$9100, $9200 |
|
|
ジャンプ先アドレステーブル |
9007 |
00 93 00 94 |
DEFW |
$9300, $9400 |
|
900B |
xx xx xx xx |
|
|
|
↓ 以降最大256アドレス分 |
|
|
これが基本的な"ON A GOTO"のプログラムです。最初にエクスチェンジ命令でデータの入っているアドレスをHLレジスタに入れます。次に HL=HL+A*2 をやって、ジャンプアドレスの入っているアドレスを計算して、そのデータをHLに入るようにして、エクスチェンジ命令でSPに入れ、RETして終わりです。 |
最後のRETの意味でずが、まずCALL命令は戻り先アドレスをスタックに積むわけですから、そこに違うアドレスを入れてRET命令を実行すれば、さもそのアドレスにj「ジャンプした」かのように見えるわけです。このプログラムの場合、そのアドレスがAの値に対応するジャンプアドレスになります。
9001と900C番地のEX(エクスチェンジ)命令ですが、これはPOP命令とPUSH命令で代用した方が高速なのですが、HLレジスタを破壊したくないため、敢えてこのようにしてあります。
念のため説明しておきますが、9000番地からが実際の使用例になっています。 |
|
リスト2 ON A GOTO (Aの範囲限定版) |
|
8000 |
E3 |
EX |
(SP) , HL |
|
|
HL = ジャンプテーブルエントリ数格納ポインタ |
8001 |
D5 |
PUSH |
DE |
|
|
DE退避 |
9002 |
5E |
LD |
E , (HL) |
|
|
E = ジャンプテーブルエントリ数 |
9003 |
23 |
INC |
HL |
|
|
HL = ジャンプテーブル先頭ポインタ |
9004 |
3D |
DEC |
A |
|
|
オフセット値-1 |
9005 |
BB |
CP |
E |
|
|
エントリ数を超えていたら、DE = エントリ数 |
9006 |
30 01 |
JR |
NC , LB01 |
|
9008 |
5F |
LD |
E ,A |
|
|
エントリ数以内なら、DE = オフセット値 |
|
LB01: |
|
|
|
|
|
8009 |
16 00 |
LD |
D , 0 |
|
|
|
800B |
19 |
ADD |
HL , DE |
|
|
HL = HL + オフセットNo. * 2 |
800C |
19 |
ADD |
HL , DE |
|
800D |
5E |
LD |
E , (HL) |
|
|
DE = 目的ジャンプ先アドレス |
800E |
23 |
INC |
HL |
|
800F |
56 |
LD |
D , (HL) |
|
8010 |
EB |
EX |
DE , HL |
|
|
HL = 目的ジャンプ先アドレス |
8011 |
D1 |
POP |
DE |
|
|
DE復元 |
8012 |
E3 |
EX |
(SP) , HL |
|
|
スタックに目的ジャンプ先アドレスを乗せる |
8013 |
C9 |
RET |
|
|
|
目的のジャンプ先へジャンプ |
|
|
|
|
|
|
|
9000 |
CD 00 80 |
CALL |
$8000 |
|
|
ON A GOTO ルーチンを呼び出す |
9003 |
04 |
DEFB |
4 |
|
|
ジャンプテーブルエントリ数 |
9004 |
00 91 00 92 |
DEFW |
$9100, $9200 |
|
|
ジャンプ先アドレステーブル × 4エントリ |
9008 |
00 93 00 94 |
DEFW |
$9300, $9400 |
|
900C |
xx xx |
|
|
|
|
↓ 以降はプログラムの続き |
|
|
リスト2は範囲を限定版です。基本的にはリスト1と同じですが、Aの範囲を限定している部分が異なります。8002~8009番地までが変更されている部分です。DEC A 以降でAの範囲オーバーをチェックしいます。1≦A≦E となるようにAをデクリメントしているのです。ここで、Aが0の場合はデクリメントされて$FFになるので大丈夫。そして、LD E , A の部分を飛ばす(つまり範囲外の場合)ときは、Eには最大値より1大きい(デクリメントを飛ばしているので)値が入っていますから、ジャンプテーブルの次のアドレスにうまく飛んでくれるというわけです。このプログラムもリロケータブルです。
今年の冬は雪が凄まじいですね。何とかならないでしょうか。
|
|
解説 (杉之原名人) |
|
|
|
|
●コンパクトでとても効率のよいプログラムに仕上がっています。本文の解説にあるように、スタックの操作はPOPとPUSHを使用した方が17クロックも高速になるのですが、HLレジスタを保持するためにEXを使用しているところが芸の細かなところです。もし、これと別にHLを保持しようとすると、確実に17クロック以上必要になるからです。
●このルーチンのさらなる高速化は、使用方法と機能に制限を設けると可能になります。ADD HL , DE という命令は11クロック消費するので、これを2回実行すると22クロックもかかります。そこでジャンプテーブル数を半分の128エントリに限定すれば、9002番地のLD命令の手前で
ADD A , A を追加して事前にオフセット値を2倍にしておくことにより、ADD HL , DE が1回で済みます。これで7クロック短縮されたことになります。ただし、Aレジスタが破壊されることになりますが。
●リスト2は非常に使い勝手の良い優秀なルーチンです。わずか20バイトのプログラムで全レジスタを保存したまま見事にテーブルジャンプ処理を実現しています。惚れ惚れします。(〃∀〃;) Aが0の場合にもジャンプテーブルの次のアドレスに飛ぶのが美味しい。Z-80プログラムの秀作といえるでしょう。
|
|
|
|
|