MC6800のプログラミングテクニック(6) 分岐条件の生成(1)

2024/12/01BASICMASTER, 昔のパソコン

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=1subb #1
AccB!=0ならC=1後述
C==1ならAccB=1,Z=0rolb / andb #1
C==0ならAccB=1,Z=0ldab #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つらい。

続く