chibicc compiler を6800向けに改造する (4) strcpy
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
ディスカッション
コメント一覧
まだ、コメントがありません