MC6800のプログラミングテクニック(17) AccBを符号拡張・Excess128,127

BASICMASTER, 昔のパソコン

昨今のC言語で普通にcharと書くとsigned charである。イマドキのCPUでは符号拡張のコストはほぼ0なので問題ないのだが、MC6800では大騒ぎである。

chibicc-6800で使っているのは下記のコード。ASR/ROLでbit7をcarryにコピーする。サイズは5bytes、実行時間は8cycle。


	clra		; 2 1
	asrb		; 2 1
	rolb		; 2 1
	sbca	#0	; 2 2


Excess 128

6502.orgのforumで知ったのだが、Excess 128というテクニックがある。

1バイトのsigned charの範囲は -128〜127 だが、これを 0〜255 に変換して作業し、最後に128を引く。浮動小数点演算の指数部の下駄履きと同じ考えだ。

この考えで、符号拡張してみよう。

eorb #$80 は (キャリーを無視すれば)addb #$80 と一緒。その後、128を引けば符号拡張できる eorb にしているのは後々の布石である。

8cyc,7bytes なので シフトした方が良さそうだが、続きがある。


	clra
	eorb #$80
	subb #128
	sbca #0


定数加算が続く場合

符号拡張のあとに定数加算が続く場合がある。例えば、配列の添字としてsigned charを使ったときだ。


int sub(char x)
{
  static unsigned char  a[] = "0123456789abcdef"; // staticなのでアドレスは定数になる

  return a[x];
}

現状のchibicc-6800だと、下記のコードになる。符号拡張して、配列のベースアドレスと足している。ベースアドレスは定数である。


        ldab 0,x
        clra
        asrb
        rolb
        sbca #0
        addb #<__L_45
        adca #>__L_45
        stab @tmp1+1
        staa @tmp1
        ldx  @tmp1
        clra
        ldab 0,x

先の、Excess 128形式の符号拡張は最後に128を引いていたが、これは-128($FF80)を足しても良い。下記のコードになる。

addb/adcaの連続はpeep hole optimizerでまとめられるので、addb #<__L_45+$80 / adca #>__L_45+$FF になる。

実質的に、符号拡張が4cyc,3bytesでできることになる。素晴らしい!


        ldab 0,x
	clra
	erob #$80
	addb #$80
	adca #$FF
        addb #<__L_45
        adca #>__L_45
        stab @tmp1+1
        staa @tmp1
        ldx  @tmp1
        clra
        ldab 0,x


減算の場合

signed charの加算はまだしも、減算だともっと大騒ぎだ。


int sub(char x)
{
  static unsigned char  a[] = "0123456789abcdef";

  return a[15-x];
}

現在の chibicc-6800 は下記のコードを出す。符号拡張して、2の補数を取って加算している。14cyc, 9bytes使う。


        ldab 0,x
        clra
        asrb
        rolb
        sbca #0
        nega
        negb
        sbca #0
        addb #<15+__L_45
        adca #>15+__L_45
        stab @tmp1+1
        staa @tmp1
        ldx  @tmp1
        clra
        ldab 0,x

unsigned charなら、アドレス計算はこれだけで済む。大差だ。


        ldab #<16
        clra
        subb 0,x
        sbca #0
        addb #<__L_45
        adca #>__L_45


減算のExcess 127

負数の場合は、ちょっとコードが変わる。eor #$80 ではなく、eor #$7f を行う。

すると、-128〜127 (減算なので+128〜-127)が、255〜0 になる。本来の値よりも127多い。これを加算して最後に127を引けば良い($ff81を足す)



先のコードで符号拡張+2の補数の部分がこうなる。8cyc,7bytes。素晴らしい。


        ldab 0,x
	eorb #$7f
        clra
        addb #<$ff81
        adca #>$ff81

配列アドレス計算まで入れると、こうなる。いやー、短い。コンパイラを修正して、このコードが出るようにしたい。


        ldab 0,x
	eorb #$7f
        clra
        addb #<$ff81+15+__L_45
        adca #>$ff81+15+__L_45
        stab @tmp1+1
        staa @tmp1
        ldx  @tmp1
        clra
        ldab 0,x