この日記はGNSで生成しています。 |
_ ということで、メールでZ80コードアンケートは24通。投稿作品が届いたので掲載しませう。この乗算ルーチンは初見でしたわ・・・。
; A*BC = BC*DE = HL が全て成立するかどうかをチェックする ; by <A HREF="http://www.yuasa.kuis.kyoto-u.ac.jp/ylab/koyama/">koyama</A> ; ; 結果は Z flag に反映される。(真なら 1、偽なら 0) ; フラグ以外のレジスタは全て元の値を維持。 (A も元のまま) ; ; 基本的な流れ: ; A ≠ DE のときは (BC=0 and HL=0) なら真、さもなくば偽。 ; A = DE のときは (A=0 and HL=0) なら真、さもなくば乗算して調べる。 ; 値の退避: ; まず DE をスタックに退避、 ; そして E を使って A を退避。 ; 乗算に入ったら DE を使って HL を退避する。 ; PUSH/POP するのは結局一回だけで済む。 ; その他: ; JR より JP が速いのはわかっているが ; ここではコードサイズの最適化を意識しているのと ; Position Independent になることを意識してみた。 COMPARE: PUSH DE ; DE の退避 CP E ; A=E かどうか LD E, A ; A の退避 (フラグは変化しない) JR NZ, BC_HL_0_CHECK ; if (A≠E) goto BC_HL_0_CHECK; XOR A ; D と E のチェックのため A ← 0 CP D ; D が 0 かどうか JR NZ, BC_HL_0_CHECK ; if (D≠0) goto BC_HL_0_CHECK; OR E ; E (退避された A) が 0 かどうか ; 比較ついでに A ← E もやってるのがミソ JR Z, HL_0_CHECK ; if (A=0) goto HL_0_CHECK; ; ; ここに辿りついた時は、 D=0, A=E になっている。 ; 逆に言うと、 POP DE すれば LD A, E で全て戻るので ; A,D,E は全部こわしてよい。 ; ; ここから乗算 ; 以下、 A の各ビットを stuvwxyz と表現することにする。 ; LD E, D ; 既に D=0 だから、これだけで DE = 0 になる EX DE, HL ; HL = 0, DE = 元のHL SCF ; Cflag = 1 (この 1 が最終的に End Mark になる) ADC A, A ;CF=s,A= tuvwxyz1, HL = 0 JR NC, MUL_LOOP2 ; s が 0 なら飛ばすので、 ; 結局 HL = BC * s になる MUL_LOOP1: ; 以下同様の理屈で、 ADD HL, HL ; ループを繰り返すごとに、以下のように変化する↓ ADD HL, BC ; A = tuvwxyz1, HL = BC * s MUL_LOOP2: ; A = uvwxyz10, HL = BC *( 2s+ t) ADD A, A ; A = vwxyz100, HL = BC *( 4s+ 2t+ u) JR Z, MUL_END ; A = wxyz1000, HL = BC *( 8s+ 4t+ 2u+ v) JR C, MUL_LOOP1 ; A = xyz10000, HL = BC *(16s+ 8t+ 4u+ 2v+ w) ADD HL, HL ; A = yz100000, HL = BC *(32s+16t+ 8u+ 4v+2w+ x) JR MUL_LOOP2 ; A = z1000000, HL = BC *(64s+32t+16u+ 8v+4w+2x+ y) ; A = 10000000, HL = BC*(128s+64t+32u+16v+8w+4x+2y+z) ; A = 00000000 で抜ける ; (ループ回数を数えるためのレジスタが不要なのがミソ) MUL_END: ; これで結局 HL = BC * A となる。 OR A ; Cflag = 0 (SUB HL,DE っていう命令が無いんだもん…) SBC HL, DE ; (乗算結果 - 元のHL) で、一致してれば Z flag が立つ。 EX DE, HL ; HL を復帰 POP DE ; DE を復帰 LD A, E ; 元は A=E なので、これで A も復帰することになる。 RET ; ; あとは HL が 0 かどうかが関わるやつ (最初の方のチェックで分岐してくる) ; BC_HL_0_CHECK: ; B,C,H,L が「全部 0」かどうか調べればいいので、 LD A, B ; B から始めて OR C ; ひたすら OR を取っていくわけですな。 HL_0_CHECK: ; (←ここから入ってくる場合は A が 0 なので、 OR H ; この OR が LD A, H と等価になるのがミソ) OR L ; この時点で、 OR 取った分が全部 0 なら Z flag = 1 LD A, E ; 退避されていた A の値を戻す POP DE ; 退避されていた DE の値を戻す RET
メールはこちらへ...[後藤浩昭 / Hiroaki GOTO / GORRY / gorry@hauN.org]