2017-03-07 18:40 — asano
カテゴリー:
3回目も昨日に引き続き、80系アセンブラの懐かしいテクニックです。
スタックポインタ(SP)を利用してメモリクリアです。
1: 8000 ORG 8000H
2: 8000 0600 LD B,0
3: 8002 110000 LD DE,0000H
4: 8005 210000 LD HL,0000H
5: 8008 F3 DI
6: 8009 39 ADD HL,SP
7: 800A 3100F0 LD SP,0F000H
8: 800D loop:
9: 800D D5 PUSH DE
10: 800E D5 PUSH DE
11: 800F D5 PUSH DE
12: 8010 D5 PUSH DE
13: 8011 D5 PUSH DE
14: 8012 D5 PUSH DE
15: 8013 D5 PUSH DE
16: 8014 D5 PUSH DE
17: 8015 10F6 DJNZ loop
18: 8017 F9 LD SP,HL
19: 8018 FB EI
20: 8019 C9 RET
これは0E000H番地から0EFFFH番地までの4KBを0クリアするルーチンです。
割り込み禁止していたり、PUSH
命令はあるのにPOP
命令がなかったり、単なるメモリクリアにしては怪しいプログラムですね。
2行目でBレジスタを0にしているのはループカウンタです。ループ1回で16バイトなので4KBクリアするには256回ループを回る必要があります。DJNZ
命令はBをデクリメントして0でなければ戻ってループする(デクリメント前にチェックはしない)なので、0は256回を意味します。
3行目は書き込む値で、0クリアなので0000Hを入れています。もし0FFHで埋めるのなら0FFFFHになります。
4行目は後で説明します。
5行目は割り込みの禁止です。これ以降、スタックポインタはスタックエリアを指さなくなるので割り込みで戻り番地をスタックに積めなくなるためです。NMI (禁止できない割り込み)が入る可能性がある場合はこのテクニックは使用できません。
6行目は現在のスタックポインタの保存です。本当はLD HL,SP
みたいな命令があればよいのですが無い(逆はあります:18行目)ので、4行目で事前にHL ⇐ 0しておいてからHL ⇐ HL+SPを実行しているのです。
7行目でスタックポインタをクリアするエリアの次のアドレスに設定します。
9~16行目で16バイト分のクリアを実行します。PUSH DE
命令がSP ⇐ SP-2; (SP) ⇐ DEという動作を利用しています。ループを展開するのは今でも使われるテクニックですよね。ループ回数が256を超えるとDJNZ
命令が使えなくなる事情もありこの例では8つ並べていますが、コードが大きくなってもよいなら16にしてループ回数を半分にしてももちろん構いません。
18行目で保存してあったスタックポインタを復元して、19行目で割り込みを許可します。
もしこれを素直に書くと
9: 800D D5 PUSH DE
のところが
9: 800D 77 LD (HL),A
10: 800E 23 INC HL
11: 800F 77 LD (HL),A
12: 8010 23 INC HL
のような形になります。もちろんレジスタの使い方も違うのでほかの部分も書き換えなくてはなりません。
Z80の場合前者は11クロックですが、後者は26クロックもかかるのでクリアするエリアが大きければメリットがあります。
一方、任意のサイズに対応させるのは面倒なのでグラフィックVRAMのクリアのようにサイズが決まっている場合でないと使いにくいですね。
コメントを追加