8080/Z80 リロケータブルコードの実験(その2) [Z80]
Twitter のタイムラインで Z80 の相対コールに関するコメントを見かけました。4年程前に書いた「8080/Z80 リロケータブルコードの実験」の記事が参照されていたので再度相対コールに関して書いてみます。
上記の記事では相対コールは速度重視の観点から DE 設定と RST 命令 の合計4バイト構成でしたが、DE設定せずに相対アドレス情報だけにすることで3バイト構成の相対コールが提案されていました(遅くはなるけど相対コール自体は短くなる)。
Z80 の相対ジャンプのように相対アドレス情報を1バイトにして2バイトの相対コールも可能ですが、元々のコール命令が3バイトなので3バイト構成の相対コールは既存のコードとの相性が良さそうです。しかし、DE/HL レジスタを破壊してしまうと既存のプログラムのコール命令を相対コールに置き換えることができません。
そこで DE/HL 等のレジスタ非破壊の相対コールを考えてみました。
流石に8バイトには収まらないので次の RST 命令を潰さないようにジャンプ命令で通常のプログラム領域に飛びます(実行ファイルを ZSID でロードできるように JP 命令自体はコメントアウトしています)。
Z80 relocatable call V2
SP はインデクス付きでのアクセスができないため HL レジスタをスタック上に保存すると復活するのが難しい(IX 等を使うと処理が更に長くなる)ので、HL はワークエリアに保存しています(DE もワーク保存の方が良かったかも)。このため割込み処理内でもこの相対コールを使う場合は RELOCALL 処理中は割込み禁止にする必要があります。
★追記 2022/08/25 {
DE をワークに保存した場合、処理は単純になりコード作成は楽ですが速度は 13 ステート遅くなり、サイズは 3 バイト(ワーク分も含めれば 5 バイト)大きくなります。
}
ZSID を使って動作確認した結果が下記になります。
relocatable call code test
上述のように RST 28H 部のジャンプ命令はコメントアウトしているので S コマンドで設定しています。
処理内で使用している DE/HL も非破壊でコール先に飛んだ後、コール先のリターン命令でコール直後の命令にリターンしていますね。
★追記 2022/08/23
HL をスタック上に保存するバージョンを作ってみました。データを積んだままスタックを INC しているので割込み禁止区間が発生します。
また、ZSID のトレースコマンド(T)は割込み禁止状態でも RST 命令を使ってレジスタをスタック上に保存しているので 正常にステップ動作できませんが G100,210 で正常に動作することを確認しました。
Z80 relocatable call V2 stack version
★追記 2022/11/30
Twitterのタイムラインで 2 バイトアドレスへの相対ジャンプの話題が出ていたので相対ジャンプについて追記します。
TLにあった IX レジスタ式のものは元々の JP 命令の 10 ステートに比べてずいぶん遅くなるので、更に遅くなりますが下記で IX 非破壊で 2 バイト短縮できます。
元々の JP 命令が 3 バイトなので同じバイト数に収めるのであれば RST を使って下記のようにもできますね(なんの捻りも無いですが)。
Z80 relocatable jump
[history]
・ver 0.01a 2022/12/24 スタックにHLが残っていたので修正
・ver 0.02 2023/01/22 DE非破壊に変更
[TOP] [ 前へ ] 連載記事 [ 次へ ]
上記の記事では相対コールは速度重視の観点から DE 設定と RST 命令 の合計4バイト構成でしたが、DE設定せずに相対アドレス情報だけにすることで3バイト構成の相対コールが提案されていました(遅くはなるけど相対コール自体は短くなる)。
Z80 の相対ジャンプのように相対アドレス情報を1バイトにして2バイトの相対コールも可能ですが、元々のコール命令が3バイトなので3バイト構成の相対コールは既存のコードとの相性が良さそうです。しかし、DE/HL レジスタを破壊してしまうと既存のプログラムのコール命令を相対コールに置き換えることができません。
そこで DE/HL 等のレジスタ非破壊の相対コールを考えてみました。
流石に8バイトには収まらないので次の RST 命令を潰さないようにジャンプ命令で通常のプログラム領域に飛びます(実行ファイルを ZSID でロードできるように JP 命令自体はコメントアウトしています)。
|
SP はインデクス付きでのアクセスができないため HL レジスタをスタック上に保存すると復活するのが難しい(IX 等を使うと処理が更に長くなる)ので、HL はワークエリアに保存しています(DE もワーク保存の方が良かったかも)。このため割込み処理内でもこの相対コールを使う場合は RELOCALL 処理中は割込み禁止にする必要があります。
★追記 2022/08/25 {
DE をワークに保存した場合、処理は単純になりコード作成は楽ですが速度は 13 ステート遅くなり、サイズは 3 バイト(ワーク分も含めれば 5 バイト)大きくなります。
}
ZSID を使って動作確認した結果が下記になります。
|
上述のように RST 28H 部のジャンプ命令はコメントアウトしているので S コマンドで設定しています。
処理内で使用している DE/HL も非破壊でコール先に飛んだ後、コール先のリターン命令でコール直後の命令にリターンしていますね。
★追記 2022/08/23
HL をスタック上に保存するバージョンを作ってみました。データを積んだままスタックを INC しているので割込み禁止区間が発生します。
また、ZSID のトレースコマンド(T)は割込み禁止状態でも RST 命令を使ってレジスタをスタック上に保存しているので 正常にステップ動作できませんが G100,210 で正常に動作することを確認しました。
|
★追記 2022/11/30
Twitterのタイムラインで 2 バイトアドレスへの相対ジャンプの話題が出ていたので相対ジャンプについて追記します。
TLにあった IX レジスタ式のものは元々の JP 命令の 10 ステートに比べてずいぶん遅くなるので、更に遅くなりますが下記で IX 非破壊で 2 バイト短縮できます。
E3 EX (SP),HL 19 ADD HL,DE E3 EX (SP),HL C9 RET |
元々の JP 命令が 3 バイトなので同じバイト数に収めるのであれば RST を使って下記のようにもできますね(なんの捻りも無いですが)。
|
[history]
・ver 0.01a 2022/12/24 スタックにHLが残っていたので修正
・ver 0.02 2023/01/22 DE非破壊に変更
[TOP] [ 前へ ] 連載記事 [ 次へ ]
コメント 0