MC6800のプログラミングテクニック(14) 多バイト整数の2の補数

BASICMASTER, 昔のパソコン

MC6800のプログラミングテクニック(1) 16bit数値の2の補数の計算
MC6800のプログラミングテクニック(7) 分岐条件の生成(2)の記事を書いてから、ずーっと考えていたことがある。多バイトの2の補数はもっと簡単に作れないか?

1バイトの2の補数はNEG命令1つでできる。2バイトだとNEGA/NEGB/SBCA #0の4バイト。


	negb	; 2 1

	nega	; 2 1
	negb	; 2 1
	sbca #0	; 2 2



ところが多バイトだと、定義通りに反転して+1するコードが使われていることが多い。なんとかならないか。

4バイト整数の2の補数

例えば、0,x – 3,x の32bit整数の補数を素直に計算すると、以下のコードになる。44-73cycle, 23bytes。最初の反転と+1とretに40cyc。あとは分岐と+1ごとに11cyc。

定義通り反転して+1している。桁上がりの検出が必要なので、bne/incを繰り返している。


	com	0,x	; 7 2
	com	1,x	; 7 2
	com	2,x	; 7 2
	com	3,x	; 7 2
	inc	3,x	; 7 2
	bne	ret	; 4 2
	inc	2,x	; 7 2
	bne	ret	; 4 2
	inc	1,x	; 7 2
	bne	ret	; 4 2
	inc	0,x	; 7 2
ret:	rts		; 5 1



普通に書くとcom 3,x / inc 3,x になるが、そこを negに変更すると少し減らせる。37-66cycle, 21bytes。

メモリ操作命令では、キャリーを使った加減算ができないので、分岐命令を使うしかない。INC with Carry のような命令があったら良かったのだが、未定義命令にも無さそう。

分岐に応じて37,48,59,66cycleかかる。遅くなるのは、$xx000000,$xxxx0000,$xxxxxx00のように下位桁が$00の場合。


	com	0,x	; 7 2
	com	1,x	; 7 2
	com	2,x	; 7 2
	neg	3,x	; 7 2
	bne	ret	; 4 2
	inc	2,x	; 7 2
	bne	ret	; 4 2
	inc	1,x	; 7 2
	bne	ret	; 4 2
	inc	0,x	; 7 2
ret:	rts		; 5 1



2,x の部分をAccBに置き換えてみる。45,45,56,63cycle, 22bytes。最初の桁で分岐が発生する場合は8cyc遅くなる。それ以外は3サイクル速い。バイト数は1バイト増える。微妙。

AccAも使ってみるパターンも考えたが、速くならないのでボツ。


	com	0,x	; 7 2
	com	1,x	; 7 2
	ldaa	2,x	; 5 2
	coma		; 2 1
	neg	3,x	; 7 2
	sbca	#0	; 2 2
	staa	2,x	; 6 2
	bne	ret	; 4 2
	inc	1,x	; 7 2
	bne	ret	; 4 2
	inc	0,x	; 7 2
ret:	rts		; 5 1



2の補数のもう一つの定義は、0から引くこと。57cyc, 24bytes. AccAも壊して良いなら、もう1バイト減らせる。

所要時間は57cycで一定。インデックスアクセスではなく ダイレクトページが対象なら、49cycleになる。バイト数は同じ。


	clrb		; 2 1
	subb	3,x	; 5 2
	stab	3,x	; 6 2
	ldab	#0	; 2 2
	sbcb	2,x	; 5 2
	stab	2,x	; 6 2
	ldab	#0	; 2 2
	sbcb	1,x	; 5 2
	stab	1,x	; 6 2
	ldab	#0	; 2 2
	sbcb	0,x	; 5 2
	stab	0,x	; 6 2
	rts		; 5 1



アクセス先がインデックス修飾を必要としないなら、Xを使う方法もある。しかし、これはインデックスアクセスを必要とする領域には使えないし、意外に遅い。59cyc,25bytes.


	com	@long+3	; 6 3
	com	@long+2	; 6 3
	com	@long+1	; 6 3
	com	@long	; 6 3
	ldx	@long+2	; 4 2
	inx		; 4 1
	stx	@long+2	; 5 2
	bne	ret	; 4 2
	ldx	@long	; 4 2
	inx		; 4 1
	stx	@long	; 5 2
ret:	rts		; 5 1

CC6303やFuzix-Compiler-Kitでは、longは @hireg:AccAB という形で持っている。

この場合は、2バイトの場合のテクニックが使える。Xを壊しても良いなら、もう少し短く・速くできる。


	com	@hireg
	com	@hireg+1
	nega
	negb
	sbca	#0
	bne	ret
	inc	@hireg+1
	bne	ret
	inc	@hireg
ret:	rts