ここでは、CC65が生成するアセンブリコードの内容をみてみましょう。
対象は、ACIA(6551)のとってもシンプルな入力用コードです。
C言語のコードは図\ref{fig:c-consc}のconin()関数です。
人によるアセンブリ言語コード
conin()関数に対応する処理を手で書いたアセンブリ言語コードを「手で書いたアセンブリ言語のコード」に示します。ステータスレジスタをポーリングして、データを受信したらそれを読み出して返すというものです。
誰が書いてもこんな感じで、これが最適解でしょうか。サイズは11バイトで、処理速度的(この処理は遅かろうが速かろうが、そもそも処理対象のシリアル通信が遅すぎるので現実的には関係ありませんが。。。)にもこれ以上は望めないように思います。
C言語の生成コードは、このコードとの比較で評価することになります。
(1) 18/ FEC8 : CONIN:
(1) 19/ FEC8 : AD 01 CC LDA ACIASR
(1) 20/ FECB : 29 08 AND #$08
(1) 21/ FECD : F0 F9 BEQ CONIN
(1) 22/ FECF : AD 00 CC LDA ACIADR
(1) 23/ FED2 : 60 RTS
手で書いたアセンブリ言語のコード
コンパイル用バッチファイル
C言語から生成されたアセンブリ言語のコードの評価を効率的に行うために、C言語のコードをコンパイルしてアセンブルリストを生成するバッチファイルcc65asm.batを作成しました。
図\ref{fig:c-cc66-asm}に示します。
2行目のcc65のオプションとしてーOが指定されています。これはコード生成時に最適化を行うことを指示するもので、最適化を行わない場合には、この-Oオプションを外しておきます。
PATH="C:\XENESIS\6502\CC65\bin"
cc65 -t none --cpu 65C02 -O %1.c -o %1.s
ca65 -t none --cpu 65C02 %1.s -l %1.lst
コンパイルとアセンブルリストの生成用バッチファイル: cc65asm.bat
バッチファイルを使用して下記のようにコンパイルすると、conin.cのアセンブルリストconin.lstが出力されます。
% cc65asm conin
プログラムのコンパイルとアセンブルリストの生成
最適化なし
C言語で書かれたconin()関数を最適化オプションなしでコンパイルした結果を「CC65の最適化なしのコード」に示します。最適化のない出力らしく、なかなか豪快な32バイトのコードが出力されています。
コードの最初と最後には、ピープホール最適化も行っていないときに出てくる典型的なコード(次の行にジャンプする:生き急がなくてもすぐに行けのに。。。)が出力されています。
また、ステータスコードの確認の前後に、jsr booleqを含む冗長なコードが出力されています。
サイズ的にも処理速度的にもいろいろ言いたいことはありそうですが、なんといっても機能的には問題がないコードをC言語で書けるのは大きな魅力です。
000042r 1 .proc _conin: near
000042r 1
000042r 1 .segment "CODE"
000042r 1
000042r 1 4C rr rr jmp L0014
000045r 1 A2 00 L0014: ldx #$00
000047r 1 AD 01 CC lda $CC01
00004Ar 1 A2 00 ldx #$00
00004Cr 1 29 08 and #$08
00004Er 1 E0 00 cpx #$00
000050r 1 D0 02 bne L001B
000052r 1 C9 00 cmp #$00
000054r 1 20 rr rr L001B: jsr booleq
000057r 1 D0 EC jne L0014
000059r 1 A2 00 ldx #$00
00005Br 1 AD 00 CC lda $CC00
00005Er 1 4C rr rr jmp L0013
000061r 1 60 L0013: rts
000062r 1
000062r 1 .endproc
CC65の最適化なしのコード
最適化あり
次に、C言語で書かれたconin()関数を最適化オプションを指定してコンパイルした結果を「CC65の最適化のコード」に示します。
ちょっとドキッとしませんでしたか?13バイトのなかなか魅力的なコードが出力されています。
結論から言うと、真ん中辺のldx #$00以外は、図\ref{fig:c-conin-asm}の手書きコードと同じです。
ちょっと残念な冗長ともいえるコードは、C言語の返却値の処理規約を守ると必要な操作なので、C言語のコンパイル結果としては完璧といっていいでしょう。
ちょっと良すぎるコードが出てびっくりしました。
コードがちょっと冗長だとか小言を言おうと思って確認したのですが、すみません、文句ないです。
00001Er 1 .proc _conin: near
00001Er 1
00001Er 1 .segment "CODE"
00001Er 1
00001Er 1 AD 01 CC L0027: lda $CC01
000021r 1 29 08 and #$08
000023r 1 F0 F9 beq L0027
000025r 1 A2 00 ldx #$00
000027r 1 AD 00 CC lda $CC00
00002Ar 1 60 rts
00002Br 1
00002Br 1 .endproc
CC65の最適化のコード
今回の例は処理内容が単純で、取り扱うデータ型char型と6502の基本処置ビット幅に合致した、ある意味特殊な条件だったので極めて優秀なコードが生成されましたが、これは出来すぎな特殊な例かもしれないことには留意が必要でしょう。