chibicc compiler を6800向けに改造する (4) strcpy

BASICMASTER, 昔のパソコン

MC6800の泣きどころはインデックスレジスタが1つしかないことと、スタック操作が苦手なことだ。例としてstrcpyを書いてみよう。


unsigned char   *strcpy(unsigned char *dst, unsigned char *src)
{
        unsigned char   *p=dst;

        while (*dst++ = *src++){
        }

        return p;
}

アセンブラでゴリゴリ書いてみる

Cソースを素直に書き下す。

もう1本レジスタがあるか、スタックポインタ相対アドレッシングがあれば、簡単に書けるプログラムなのだが、MC6800では長くなってしまう。


_strcpy:
        tsx
        ldab    3,x     ; dst
        ldaa    2,x     
        pshb
        psha            ; p: 0,s
loop:   tsx
        ldx     6,x     ; src
        ldab    0,x
        tsx
        inc     7,x
        bne     L1
        inc     6,x
L1:     tsx
        ldx     4,x     ; dest
        stab    0,x
	tsx
        inc     5,x
        bne     L2
        inc     4,x
L2:     tstb
        bne     loop
L3:     pulb
        pula
        rts

ポストインクリメント操作をまとめて行えば、さらに小さく高速になる。

コンパイラでこのコードを出すには、ASTをいじって、++を代入の後に移動すれば良いか? 難しそうだ。


_strcpy:
        tsx
        ldab    3,x     ; dst
        ldaa    2,x     
        pshb
        psha            ; p: 0,s
loop:   tsx
        ldx     6,x     ; src
        ldab    0,x
        tsx
        ldx     4,x	; dst
        stab    0,x
        beq     L3
        tsx
        inc     7,x     ; src
        bne     L1
        inc     6,x
L1:     inc     5,x     ; dst
        bne     loop
        inc     4,x
        bra     loop
L3:     pulb
        pula
        rts

CC6303の lib6800/_strcpy.s

src/dstをゼロページにコピーして使っている。リエントラントを考えなければ、これが良い。


_strcpy:
        tsx
        ldx     4,x             ; dest
        stx     @tmp
        tsx
        ldx     2,x             ; src
copyloop:
        ldab    ,x
        inx
        stx     @tmp2
        ldx     @tmp
        stab    ,x
        inx
        stx     @tmp
        ldx     @tmp2
        tstb
        bne copyloop
        tsx
        ldab    5,x             ; return dest
        ldaa    4,x
        jmp     ret4


CC6303のMC6800コード

CC6303でstrcpy.cをコンパイルした出力コード。pshindvx4は、4,X/5,Xをスタックに積むサブルーチン。

++ 操作を先に実行して、ポインタを使う前に -1 している。


_strcpy:
	tsx
	jsr pshindvx4
L0003:
L0005:
	tsx
	ldaa $06,x
	ldab $06+1,x
	addb #$01
	adca #$00
	staa $06,x
	stab $06+1,x
	subb #$01
	sbca #$00
	pshb
	psha
	ldaa $04,x
	ldab $04+1,x
	addb #$01
	adca #$00
	staa $04,x
	stab $04+1,x
	subb #$01
	sbca #$00
	jsr dtoxclra
	ldab $00,x
	jsr dopulxstb
	tstb
	jne L0003
L0004:
	jsr loadtos
L0001:
	ins
	ins
	jmp ret4


Fuzix Compiler Kit の出力コード

++操作の前に、dstポインタの値をスタックに積んでおき、実際に利用する前にpulしている。

MC6800にはIXをpulする命令が無いので、TSX/LDX 0,X/INS/INS になっている。

srcポインタは加算結果をDEXして利用している。src側もDEXで良いはずだが、最適化できていない。


_strcpy:
	des
	des
	tsx
	ldb 5,x
	lda 4,x
	stb 1,x
	sta 0,x
;
L1_c:
	tsx
	ldb 5,x
	lda 4,x
	pshb
	psha
	addb #1
	adca #0
	stb 5,x
	sta 4,x
;	delete pula/pulb/pshb/psha
	ldb 7,x
	lda 6,x
	addb #1
	adca #0
	stb 7,x
	sta 6,x
	ldx 6,x
	dex
	ldb 0,x
	tsx
	ldx ,x
	ins
	ins
	stb 0,x
	jeq L1_b
	jmp L1_c
L1_b:
	tsx
	ldb 1,x
	lda 0,x
;
L0_r:
	ins
	ins
	jmp __cleanup4


z88dkのstrcpy.s

(hl+) とか (de+) は、アセンブラが頑張ってるみたい。

レジスタ多いマシンは有利だよなあ。


asm_strcpy:
   
   ; enter : hl = char *s2 = src
   ;         de = char *s1 = dst
   ;
   ; exit  : hl = char *s1 = dst
   ;         de = ptr to terminating NUL in s1
   ;
   ; uses  : af, bc, de, hl

   push de

IF __CPU_INTEL__ || __CPU_GBZ80__

loop:
   ld a,(hl+)
   ld (de+),a

   or a
   jr NZ,loop

ELSE

   xor a
loop:
   cp (hl)
   ldi
   jr NZ,loop

ENDIF
   pop hl
   dec de
   ret