MC6800のプログラミングテクニック(6) 分岐条件の生成(1)
Fuzix-Compiler-Kit の Cコンパイラ(fcc)の現在のバージョンは、分岐の場合は下記のようなコードを生成する。
比較がサブルーチンになっているのは、オブジェクトサイズを小さくするためと、分岐条件を beq/bne (Z flag)に揃えて && や || を楽にするためである。たぶん。
しかし、単純な分岐でサブルーチンを呼ぶのはいかにも重たい。なんとかしたい。
(JSR〜RTSだけで14cycle,4bytesも必要)
if (a) の場合: ldab _a+1 ldaa _a jsr __bool jeq false ; 不成立 if (a<b) のような比較の場合: ldab _a+1 ldaa _a pshb psha ldab _b+1 ldaa _a+1 jsr __cclt jeq false ; 不成立
AccA,Bが両方0ならZを立てる(それ以外はZ=0)
こんなコードを考えてみた。3バイト。
AccA,Bが両方0ならABA(A=A+B)の結果は0。0以外の場合は結果は1-510。結果が256以上(256-510)の場合は、AccAの値は0-254になりキャリーが立つ。
そこでキャリーを足し込むと、1-255 になる。0にならないのがミソ。
aba adca #0 jeq false
AccBが0ならZ=0,AccB=1(それ以外はZ=1,AccB=0)
いきなり2バイトを考えるのは大変なので、1バイトで考える。
AccBから1を引くと、AccB==0のときだけキャリーが立つから、それを0,1に変換する。副作用としてbool値も得られる。6cycle, 5bytes.
AccB==0ならZ=0: subb #1 rolb andb #1
素直に書くと、8 or 12cycle, 7bytes.
tstb beq ret1 ret0: clrb bra done ret1: incb done:
AccA,Bが両方0ならZ=0、AccB=1(それ以外はZ=1、AccB=0)
さて、元に戻ってAccA,Bの両方が0のときにZ=0にするルーチンだが、前述の2つを組み合わせれば良い。
一見するとわけがわからないので解説しておく。
aba/adca #0 で、AccA,Bが両方0ならAccAは0になり、そうでなければAccA!=0である。
あとは1を引いて、AccA==0のときだけCを立て、rolb/andb で AccBに0 or 1を作る。10cycle,8bytes.
aba adca #0 suba #1 rolb andb #1
素直に分岐で書くとこうなる: 8 or 14 or 18cycle, 10bytes. 元々AccA==0のときだけは、こちらが速い。
tsta bne ret0 tstb bne ret0 incb bra done ret0: clrb done:
AccA,Bが両方0ならZ=1、AccB=0(それ以外はZ=0、AccB=1)
逆演算。最後を引き算にして、1から引くことでAccBに設定している。10cycle,9bytes.
rolを使うと、最後にeorb #1が必要になり 1バイト増えてしまうので、sbcbを使った。
aba adca #0 suba #1 ldab #1 sbcb #0
追記:
NEGを使う案を思いついた。10cycle, 7bytes. 実行時間は変わらないが、2バイト減った。
aba adca #0 nega ; if AccA==0 then C=0 else C=1 rolb andb #1
要するにZフラグとキャリーの相互変換
結局、これらの操作は何をやっているかというと、Accが0であるかどうかと、キャリーの有無を相互に変換しているのである。
操作 | コード |
---|---|
AccB==0ならC=1 | subb #1 |
AccB!=0ならC=1 | 後述 |
C==1ならAccB=1,Z=0 | rolb / andb #1 |
C==0ならAccB=1,Z=0 | ldab #1 / sbcb #0 |
AccB!=0のときだけCを立てるのが難しい。無理やり書くなら、subb #1 / rolb / eorb #1 / rorb か。
subb #1 rolb erob #1 rorb
これだと、bには値が残らないのがもったいないので、Cに応じたbool値を残してみる。asrbを使って、bit7をbit6にコピーするお馴染みの手順である。
subb #1 rorb asrb eorb #$c0 andb #$c0 rolb rolb
素直に分岐で書いた方がマシだった。
tstb beq done ldab #3 rorb don:
追記:
「AccB!=0のときだけCを立てるのが難しい」と書いたが嘘だった。NEGB一発である。
しかし、MC6809だとCMPDがあるから、苦労しないで良いんだよねえ。6800つらい。
続く
ディスカッション
コメント一覧
まだ、コメントがありません