sdcc
PIC日記← →SDCCを飼い馴らす →PIC18F4550 mcc18
SDCC(Small Device C Compiler)とは†
一次情報源:
- Intel8051*1*2*3マイコン用に作られたオープンソースのCコンパイラです。
- 現在ではIntel8051以外に、Maxim 80DS390, Z80 , 68HC08 がサポートされていて、 PIC16F , PIC18F シリーズもサポートされつつあります。
- 2008-03-22にリリースされているバージョン sdcc-2.9.0 は PIC 18F14K50チップのサポートがありません。
- 以下の最新スナップには、18F14K50が含まれているようです。
http://sdcc.sourceforge.net/snap.php
目次
SDCCによるUSBアプリケーションのビルド†
read more : SDCCによるUSBアプリケーション
続:SDCCを飼い馴らす。†
この記事に たいした期待をしないでください
- まず、
strcpy(char *t,char *s) { while( *t++ = *s++ ); }
のような関数を書いて、sdccに掛けます。
- 出来たアセンブルリスト(*.LST) を3分間眺めます。
- _gptrset1,_gptrget1 に興味を持ちます。
- それらのソースはsdccをインストールしているなら以下にあります。
C:/sdcc/lib/src/pic16/libsdcc/gptr/*.c
- インストール先がc:/sdcc/でない場合は読み替えてください。
- far ポインタは3バイト長です。 AVRの場合と違い、ROM/RAMをMSBで判別します。
- EEPROMは未実装です。
はい、ここまでで気づきますね。C言語ではまともなパフォーマンスが出ません。
C:/sdcc/lib/src/pic16/
- 上記場所にライブラリのソースがあります。一通りさらっと目を通しましょう。
では、本題です。
- 関数名を定義したら、 __naked にしましょう。
- 注意: __nakedにした場合は return もasm内に記述しないといけません。
- もちろん、関数内にラベルを書いて、rcall することさえ可能です。
- 関数呼び出しは rcall で行うと+-1024バイトまでの距離は分岐できます。
- 2Kを超えるプログラムを書くときは、どうやら呼びたい関数が近くにあることを保証できないようです。rcallは(局所的な使用以外は)諦めましょう。
- asm内のラベルは、後ろに ':'(コロン)を書いて識別する必要があります。
- (MPASMでは行頭に書けば':'を省略できるようですが・・・)
- 関数の中身は __asm と __endasm で囲みましょう。
void sub1(void) __naked { __asm 処理を記述 ・・・ __endasm ; }
- 変数は '_' (アンダースコア) を先頭につける必要があるようです。
- 上記のライブラリソースの書き方を見て真似をすると良いです。
- 引数渡しなどでユーザースタックが欲しくなったら、FSR1 を使います。
- FSR2はsdccではスタックフレームポインタとして使われています。
- 関数のエントリーで FSR2L = FSR1L のようなレベルあわせが行われます。
- FSR1H = FSR2H = 0x01 固定のようです。
それってまんまASMで書こうって言ってるのと同じ・・・
C言語記述を使ってもいい処理だってある。†
- I/Oポートの制御は一応ビット命令になりますのでASM記述とたいして変わりません。
- 8bit変数の操作や16bitカウンタなどの操作に関してもASM記述とたいして変わりません。
- C記述(__nakedを付けない関数)の場合、仮想的なレジスタ(使用する分だけ)のPUSH/POPが関数の入り口と出口で発生します。また、スタックポインタをスタックフレームにコピーする処理や、引数をスタックフレームから参照して仮想的なレジスタ(r0x01とか。実際はメモリー)にコピーする処理が無駄に入ります。
- それらが許容できる程度の問題であれば、C記述を使うことが出来ます。
- パフォーマンスを上げたい部分だけ __naked という選択肢だってあります。
SDCCにもの申す†
- いや、そんな。滅相もありません。
- むしろ、これだけコンパクトに記述されたコンパイラなのに
- きちんと動いていて、PIC18Fのようなへぼいマイコンをサポートして くれていることに感謝しないわけにはいきません。
が、どうすればさらに良くなるか考察してみましょう。
- 仮想的なレジスタ(0番地から最大0x60バイトまで確保されるr0xXXという名前のワーク)をセーブレジスタとテンポラリレジスタに分ける。
- むしろテンポラリレジスタを積極的に使い、スタックには保存しないようにする。
- 割り込み処理ではテンポラリレジスタの退避復帰が義務付けられてしまう。
- このへんは普通のレジスタの多いCPUと事情は同じ。
- FSR2(スタックフレーム)を使わないようにする。
- FSR1(スタックポインタ)だけで出来る場合はなるべくFSR1相対だけでやる。(やれるような気がする)
- gccでもomit-frame-pointerという最適化ですでに実現されている。
- FSR2の退避復帰がなくなるだけでも少し軽くなる。
- FSR1のPOPを適度にサボる。(これはgccの最適化がそのようになっていて、引数スタックを戻す処理は遅延実行でまとめてやるのだ。)
- callのrcall化をやって欲しいなぁ・・・。(届く場合のみ)ただしこれはlinkerの仕事になる。(relax link)
*1 元祖8bitMCUとも呼べる。祖先はi8048であり、IBM-PCのキーボードコントローラーとして使われた。今ももしかしたらintelのチップセットの片隅に残っているのかもしれない
*2 FPGAのIPコアとしても存在していて、100MHzオーバーで動くものもある
*3 CypressのEzUSBチップは8051アーキテクチャを採用している