条件比較の結果を、Accに入れる話の続きである。
今回は、strcmpのように 負数と正数 を入れたい場合の話。浮動小数点演算でも必要になる。
- MC6800のプログラミングテクニック(6) 分岐条件の生成(1)
- MC6800のプログラミングテクニック(7) 分岐条件の生成(2)
- MC6800のプログラミングテクニック(8) 分岐条件の生成(3)
- MC6800のプログラミングテクニック(19) AccABと0の比較
strcmpはどう書くか(Cバージョン)
strcmpは2つの文字列を文字コード順に比較し、大小関係を 負、0、正の値で返す。
文字列末の判定やポインタ増加のタイミング、戻り値の計算手法でバリエーションはあるが、概ね下記のコードになる。
int strcmp(const char *s1,const char *s2) { while (*s1 == *s2) { if (*s1) { return 0; } s1++; s2++; } return (unsigned char)*s1 - (unsigned char)*s2; }
アセンブラバージョン(抜粋)
1バイトずつ先頭から見ていくのであれば、引き算して符号拡張して返せば良い。
しかし引き算するとレジスタが壊れてしまうので、なるべく比較を使いたい。符号拡張のタイミングで再計算しても良いが少々ダサい。
また、MC6800はポインタが1つしかないので、1バイト比較ごとにstx/ldxで切り替えると遅くなる。せっかくレジスタは2つあるのだからうまく活用したい。
高速化のためにAccA,Bの両方を使うと下記のプログラムになる。減算ではなく比較なので、符号拡張できない。
仕方がないので、-1 と 1 を固定で返すようにしている。C言語の規格では負と正であれば良いので、これで問題ない。
_strcmp_loop: ldx @tmp2 ldab 0,x ldaa 1,x inx inx stx @tmp2 ldx @tmp3 cmpb 0,x bne _strcmp_ne tstb beq _strcmp_eq cmpa 1,x bne _strcmp_ne inx inx stx @tmp3 tsta bne _strcmp_loop _strcmp_eq: clrb clra rts ; return 0 _strcmp_ne: bcs _strcmp_lt ldab #1 ; return 1 clra rts _strcmp_lt: ldab #$FF ; return -1 tba rts
高速化&省サイズ化
上記のルーチンは分岐命令(branch if carry set)を使って C=1 なら -1 を C=0 なら 1 を返している。
_strcmp_ne から最後の rts まで計10バイト。実行時間はどちらの分岐でも13cycである。
せっかく C に情報があるから、これを使ってみる。$00 から C を引けば $FF(C=1)、$00(C=0)になる。これを細工する。
_strcmp_ne: ldab #0 sbcb #0 ; 00 or ff tba orab #1 ; 00->01, ff->ff rts
最後の orab #1 で $00を$01にしている。$FF のときは変わらない。
これで、13cyc, 8bytesである。実行時間は一緒でサイズが2バイト減った。
AccAのときとBのときで処理を分ける?
同一ルーチンで処理するのをやめれば短くなる。どのみち符号しか見ないのだから、AccAに入れて再計算すれば良かろう。符号だけが必要だからAccBの値はなんでも良い。
実行時間は 10 or 12cyc、7bytes。インデックス修飾が遅いんだよな、MC6800。
_strcmp_loop: : cmpb 0,x bne _strcmp_ne_b tstb beq _strcmp_eq cmpa 1,x bne _strcmp_ne_a (中略) : _strcmp_ne_b: tba suba 0,x rts _strcmp_ne_a: suba 1,x rts
dexを使ってまとめると2バイト減るが、4cyc増えてしまう(16cyc)。inx/dexも遅い。
_strcmp_ne_b: tba dex _strcmp_ne_a: suba 1,x rts

コメント