Z80で平方根を求めるには?

A = SQRT(BC) を計算するプログラムです。
AF,BC,DE,HLレジスタが破壊されます。裏レジスタ・インデックスレジスタは保存されます。
SQRT_BC:
    XOR     A
    LD      D,A
    LD      E,A
    LD      L,A
    LD      H,A
    LD      (SQRT_BC_RESULT),A
    LD      A,8
SQRT_BC_LOOP:
    ; BC の上位2bit を HL の下位に移動
    RLC     C           ; 最下位にゴミが入るが気にしない。RL より RLC の方が速い。
    RL      B
    RL      L
    RL      H
    RLC     C           ; 最下位にゴミが入るが気にしない。RL より RLC の方が速い。
    RL      B
    RL      L
    RL      H
    PUSH    BC
    ; DE を左1bit シフトして下位に 1 を立ててみる
    RLC     E
    RL      D           ; Cy = 0
    SET     0,E         ; Cy不変
    ; HL - DE を評価する
    SBC     HL,DE       ; 試しに引いてみる
    JP      NC,SQRT_BC_SKIP1
    ADD     HL,DE       ; 引けなかった場合は足し戻す。必ず Cy = 1 になる。
    DEC     DE          ; 上の SET 0,E をキャンセルする。Cy不変。
    DEC     DE          ; 下の INC DE をキャンセルする。Cy不変。
SQRT_BC_SKIP1:
    INC     DE          ; 答えに 1 が立つ場合、DE に 1加算。Cy不変。
    LD      B,A
    LD      A,(SQRT_BC_RESULT)
    CCF                 ; Cy反転すると、答えの着目桁のビットになる
    RLA                 ; 答えを SQRT_BC_RESULT の最下位に取り込む
    LD      (SQRT_BC_RESULT),A
    LD      A,B
    POP     BC
    DEC     A
    JP      NZ,SQRT_BC_LOOP
    LD      A,(SQRT_BC_RESULT)
    RET
SQRT_BC_RESULT:
    DB      0
		

一番最初だけ実施する特別処理も、初期値やシフトの位置を工夫すると繰り返し部分と同じになります。
演算対象の値は BC に詰まってますが、左シフトによって 2bit 切り出して HL の下位 2bit に追加します。
DE には、開平法の左側に相当する値を詰めていて、set 0,E で「右端に 1」を追加してます。試しに HL から引いてみます。
引けた場合 (Cy=0) は、答えのビットは 1 に、引きすぎた場合=引けなかった場合 (Cy=1) は、答えのビットは 0 になります。
引きすぎた場合は、引いた値 DE を足し戻して元に戻すと同時に、DE の右端に追加すべき値は 1 ではなく 0 だったということで
dec DE によって「右端を 0」にします。res 0,E でも良いですが、dec DE のが速いのでこちらを選んでます。
さらに、開平法では立てた答えのビットを加算することになっていて、下の方で inc DE してます。
しかし、引けなかった場合は inc DE したくないのですが、分岐命令を何度も実行したくないので inc DE をキャンセルする意味合いで dec DE してます。
inc DE を通過した時点で、先ほどの引けた引けないの結果である Cyフラグは破壊されずに保存されているので、 これを反転して答えのビットとして最下位に取り込みます。
これを 答え8bit のビット数だけ繰り返しています。

次は少しだけ最適化します。

[前へ][戻る][次へ]