chibicc compiler を6800向けに改造する (9) IEEE754 float の比較(3)
chibicc 6800のfloatの比較では、TOS (top of stack)に左辺を積んで、右辺を@longに置いて、jsr __cmpf32tos する。
cmpf32tos は AccB とフラグで比較の結果を返す。
両辺のどちらかが NaNの場合は、C=1 を返す。これは、浮動小数点数の比較演算では NaNがあれば常に不成立 であるから。
普通に考えれば if ( (a==b) || (a!=b) ) は常に成立するように見えるが、floatのときはどちらも偽になることがある。
cmpf32の返り値
条件 | 成立時 |
---|---|
< | AccAB=$ffff |
== | AccAB=0 |
> | AccAB=1 |
さて、帰ってきた情報を元に、どのように分岐を組み立てるのかが問題だ。
Cの条件式と同様に、成立時は1, 不成立時は0にしたい。
EQ (==)
素直に考えれば Z=1 なのだから、beq/bneで分岐して、分岐先で1か0を読めば良い。
良いのだけど、無駄が多い。9バイトも使っているし、遅い。
jsr cmpf32tos beq L1 clra clrb bra L2 L1: ldab #1 clra L2:
AccBが0のときだけ Bを1にするのだから、1を引いたキャリーを使えば良い。6バイト8サイクル。最後の andb でZが変化するので、分岐にも使える。
clra subb #1 rolb andb #1
書いていて気がついたけど、こっちが1バイト短い。1も$FFもbit0が立っていることを利用する。バイト6サイクル。
eorb の代わりに addb でも良いけど、eorb の方がわかりやすいと思う。
clra eorb #1 andb #1
NE (!=)
AccBが0のときは、そのまま、1と$FFのときは1にしたいのだから、これで良い。超簡単。
clra andb #1
LT (<)
AccBが$FFのときは、AccB=1, それ以外は0である。 1を足すと、$FF だけキャリーが立つことを利用する。
clra addb #1 rolb andb #1
記事を書いていると、いろいろと思いつく。0と1は2で割れば0。$FFは$7Fで、最下位ビットが立つので、これで良い。2バイト短くなった。
clra lsrb andb #1
GT (>)
AccBが1のときだけ、AccBを1にしたい。0と$FFのときは0だ。分岐を書いて良ければ簡単。8バイト。分岐時10クロック、非分岐時12クロック。
clra cmpb #1 beq L1 clrb L1: andb #1
ちょっと難しいが、0から1を引くと0のときだけキャリーが立つことを利用する。ひとつずらすためにdecbを使う。7バイト、10クロック。
clra decb subb #1 rolb andb #1
まだ短くできた。
1を足して、$FF,0,1 を 0,1,2 にしてから1ビット右シフトすると 0,0,1 になる。最後のandも要らない。
clra incb lsrb
LE (<=)
$FF,0 のときだけ1にしたい。1を引いて $FE,$FF,$00 にしてから2を足すと 成立時だけ C=1 になる。
clra decb addb #2 rolb andb #1
FE,FF,00 だと前2者だけbit7が立っていることが利用できることに気がついた。1バイト短くなった。
clra decb aslb rolb andb #1
GE (>=)
0,1 のときだけ1にしたい。2を引けばOK
clra subb #2 rolb andb #1
ディスカッション
コメント一覧
まだ、コメントがありません