PIC用Cコンパイラ無償版の「char型の扱い」
2012-12-02
「アセンブラ信者の宗旨替え」から一晩経って早速「C言語信者」になるべく、アセンブルリストを見ながら展開形(C言語の1行がどの程度のニーモニックに置き換えられるのか)のチェックをしました。各種のPIC用C言語コンパイラ製品版は非常に高価で、一応Microchip社推しの「XC8」はStandardが495$
Proが1195$
・・・幾ら円高でも趣味の範疇では少々ビビってしまいます
そこで、無償版のオプティマイズの実力を知っておいて、できるだけコンパクト(≒高速)なプログラムを作るための予備知識を蓄えておこうという魂胆です。
XC8の製品版の「売り文句」としての「Optimization Levels」は、無償版の2倍がStandard、4倍がProというイメージで、確かにその魅力はあるんでしょうが、コンパイラとしての得手・不得手を捕まえてしまいその欠点を補うようなコーディングをすれば、ある程度自前で「無碍な肥大化」を防ぐことができます。また、インラインアセンブラの活用場面の検討にも有利になりますから、この調査はやっておいて損はないでしょう。
そこで、とりあえずXC8の最新バージョン(Ver 1.11)を使って調査してみました。
◆ 調査の前に・・・
・ Enhanced Mid-Rangeを使う
上位言語の特性として「関数」という考え方が強くなります。関数は「戻り値が得られる」「パラメータを渡せる」という部分がその特徴であり、戻り値とパラメータの引き渡しの部分で、旧来のMid-Rangeモデルには幾つか欲しいニーモニックがあったわけですが、これを「Enhanced Mid-Range」ではかなり意識して改善させています。既にPIC16F18xx/19xxは安価で入手も容易、かつ既にPIC16F1823,1827が「我が定番」になりそうですから、まずは「PIC16F1827」を前提に調査しました。
・ あくまで8ビットRISCチップであるということ
C言語の「int」は、これまでのプロセッサの歩みとC言語登場の歴史から16ビット/32ビットが主流なわけですが、幾ら「Enhanced・・・」とは言え8ビットRISCチップに代わりはありませんから、処理が得意なのは明らかに「char」になります。従って、8ビット以上の処理は「それなりの数のニーモニック」にコンパイルされますので、この点も「無駄な処理」になる可能性があります。
そもそも、8ビットもあれば「256通りの意思疎通」ができるわけで、特殊な処理でもない限りはこの範疇で収まるように考えるべきでしょう。
◆ ポートアクセスについて
まずは、何と言ってもポートアクセスについての記述・・・これが解り易いことに加えて、収容バンクの切替などがどんな風なのか・・・という部分です。
記述自体は、上記の通り「レジスタ名」がそのまま使えるようにヘッダファイルが用意されていますから、アセンブラ同様解り易いですね。上記の展開形はというと・・・
アセンブルリストから、ニーモニックに関係のない行・コメントを削除していますが、かなり綺麗にコンパイルされています。
・ バンク切替命令は、必要なときに適宜挿入
・ 8ビットの0設定にclrfを使用
・ ビット制御にはbcf/bsf命令
アセンブラで作っても、上記の通りになりそうですから合格でしょう。バンク切り替えができるだけ生じない順序のコーディングを意識するだけで十分なようです。
◆ 変数代入の罠
8ビット操作が得意なわけですから、8ビットの変数への数値代入も上記のようになるだろうと高を括っていましたが、やはり一筋縄ではいきませんでした
まず、C言語ソースは以下の通り。外部変数「a,c」と内部変数「bb,dd」に数字を代入です。
これがどんな風にコンパイルされるかというと・・・
0,1はそれなりに考えられた最適化が掛かっていますが、即値は2ステップ分のロスがあります。これは実にポピュラーな処理ですから頻発必至・・・ちょっと萎えてしまいました。そこで、あれこれ情報を当たってみたら「HI-TECH C Ver 9.70」では、少し違ったニーモニックが・・・。
即値の代入は、「HI-TECH C Ver 9.70」ではwレジを介した2ステップ・・・普通に考えられるステップ数で片付きます。が、0,1を代入する処理がねぇ・・・。ちなみに、HI-TECH Cの上記以降のバージョンはXC8と同じように「4ステップ」になっています。
また、変数から変数への代入は「HI-TECH C Ver 9.70」はwレジを介した2ステップ、XC8はワーク変数を介した4ステップですから、「0,1の処理に勝るXC8」と甲乙付け難く、ちょっと迷ってしまいます。
何れにせよ、XC8の「char型への2以上の数値代入」「char型同士の代入」は4ステップを消費・・・これはあんまり歓迎できませんね。
◆ 関数の戻り値は「char型のBoolean」が良さそう
関数処理結果の「真偽の受け渡し」を戻り値で行う場合、intなどの型では大きすぎてコンパイル結果のニーモニックが冗長になるため、char型のBooleanを作ると良さそうです。if文の分岐もwレジスタだけで分岐できますので好都合
何だかんだ詳しく調べたり環境の入れ替えを頻々と行っていたりしたら、結局char関連の理解だけで今日は終わってしまいました・・・。が、多分それ以上のビット数の処理は「推して知るべし」でしょうから、この記事の留意点だけで行けそうな気もしています。
※ 12/12/03 Booleanについては流石に考慮してあり、「stdBool.h」をincludeすると「bool」という関数属性が使えます。



XC8の製品版の「売り文句」としての「Optimization Levels」は、無償版の2倍がStandard、4倍がProというイメージで、確かにその魅力はあるんでしょうが、コンパイラとしての得手・不得手を捕まえてしまいその欠点を補うようなコーディングをすれば、ある程度自前で「無碍な肥大化」を防ぐことができます。また、インラインアセンブラの活用場面の検討にも有利になりますから、この調査はやっておいて損はないでしょう。
そこで、とりあえずXC8の最新バージョン(Ver 1.11)を使って調査してみました。
◆ 調査の前に・・・
・ Enhanced Mid-Rangeを使う
上位言語の特性として「関数」という考え方が強くなります。関数は「戻り値が得られる」「パラメータを渡せる」という部分がその特徴であり、戻り値とパラメータの引き渡しの部分で、旧来のMid-Rangeモデルには幾つか欲しいニーモニックがあったわけですが、これを「Enhanced Mid-Range」ではかなり意識して改善させています。既にPIC16F18xx/19xxは安価で入手も容易、かつ既にPIC16F1823,1827が「我が定番」になりそうですから、まずは「PIC16F1827」を前提に調査しました。
・ あくまで8ビットRISCチップであるということ
C言語の「int」は、これまでのプロセッサの歩みとC言語登場の歴史から16ビット/32ビットが主流なわけですが、幾ら「Enhanced・・・」とは言え8ビットRISCチップに代わりはありませんから、処理が得意なのは明らかに「char」になります。従って、8ビット以上の処理は「それなりの数のニーモニック」にコンパイルされますので、この点も「無駄な処理」になる可能性があります。
そもそも、8ビットもあれば「256通りの意思疎通」ができるわけで、特殊な処理でもない限りはこの範疇で収まるように考えるべきでしょう。
◆ ポートアクセスについて
まずは、何と言ってもポートアクセスについての記述・・・これが解り易いことに加えて、収容バンクの切替などがどんな風なのか・・・という部分です。
PORTA = 0; // BANK0 PORTB = 12; TRISA0 = 0; // BANK1 TRISB1 = 1; |
記述自体は、上記の通り「レジスタ名」がそのまま使えるようにヘッダファイルが用意されていますから、アセンブラ同様解り易いですね。上記の展開形はというと・・・
;test.c: 18: PORTA = 0; movlb 0 ; select bank0 ;; バンク切り替え clrf (12) ;; 0を設定⇒clrfを使用 ;test.c: 19: PORTB = 12; ;; バンクが変わらないため」バンク切り替え命令はない movlw (0Ch) ;; 12を代入(アセンブラでも同様な記述) movwf (13) ;test.c: 21: TRISA0 = 0; movlb 1 ; select bank1 ;; バンクが変わればバンク切り替え bcf (1120/8)^080h,(1120)&7 ;; ビットに0を設定⇒bcf使用 ;test.c: 22: TRISB1 = 1; ;; バンクが変わらないため処理はない bsf (1129/8)^080h,(1129)&7 ;; ビットに1を設定⇒bsf使用 |
アセンブルリストから、ニーモニックに関係のない行・コメントを削除していますが、かなり綺麗にコンパイルされています。
・ バンク切替命令は、必要なときに適宜挿入
・ 8ビットの0設定にclrfを使用
・ ビット制御にはbcf/bsf命令
アセンブラで作っても、上記の通りになりそうですから合格でしょう。バンク切り替えができるだけ生じない順序のコーディングを意識するだけで十分なようです。
◆ 変数代入の罠
8ビット操作が得意なわけですから、8ビットの変数への数値代入も上記のようになるだろうと高を括っていましたが、やはり一筋縄ではいきませんでした

まず、C言語ソースは以下の通り。外部変数「a,c」と内部変数「bb,dd」に数字を代入です。
#include <pic16f1827.h> char a,c; main() { static char bb; static char dd; a = 0; c = 1; bb = 5; dd = 0; } |
これがどんな風にコンパイルされるかというと・・・
;test.c: 10: a = 0; clrf (_a) ;; 0はclrf ;test.c: 11: c = 1; clrf (_c) ;; 1はclrfして1インクリメント incf (_c),f ;test.c: 12: bb = 5; movlw (05h) movwf (??_main+0)+0 ;; 即値設定はここを介して行う movf (??_main+0)+0,w movwf (main@bb) ;test.c: 13: dd = 0; ;; 外部変数とローカル変数は同じ仕様 clrf (main@dd) |
0,1はそれなりに考えられた最適化が掛かっていますが、即値は2ステップ分のロスがあります。これは実にポピュラーな処理ですから頻発必至・・・ちょっと萎えてしまいました。そこで、あれこれ情報を当たってみたら「HI-TECH C Ver 9.70」では、少し違ったニーモニックが・・・。
;test.c: 10: a = 0; clrc movlw 0 btfsc status,0 movlw 1 movwf (_a) ;test.c: 11: c = 1; clrf (_c) bsf status,0 rlf (_c),f ;test.c: 12: bb = 5; movlw (05h) ;; 即値は2ステップで片付く movwf (main@bb) ;test.c: 13: dd = 0; clrc movlw 0 btfsc status,0 movlw 1 movwf (main@dd) |
即値の代入は、「HI-TECH C Ver 9.70」ではwレジを介した2ステップ・・・普通に考えられるステップ数で片付きます。が、0,1を代入する処理がねぇ・・・。ちなみに、HI-TECH Cの上記以降のバージョンはXC8と同じように「4ステップ」になっています。
また、変数から変数への代入は「HI-TECH C Ver 9.70」はwレジを介した2ステップ、XC8はワーク変数を介した4ステップですから、「0,1の処理に勝るXC8」と甲乙付け難く、ちょっと迷ってしまいます。
何れにせよ、XC8の「char型への2以上の数値代入」「char型同士の代入」は4ステップを消費・・・これはあんまり歓迎できませんね。
◆ 関数の戻り値は「char型のBoolean」が良さそう
関数処理結果の「真偽の受け渡し」を戻り値で行う場合、intなどの型では大きすぎてコンパイル結果のニーモニックが冗長になるため、char型のBooleanを作ると良さそうです。if文の分岐もwレジスタだけで分岐できますので好都合

何だかんだ詳しく調べたり環境の入れ替えを頻々と行っていたりしたら、結局char関連の理解だけで今日は終わってしまいました・・・。が、多分それ以上のビット数の処理は「推して知るべし」でしょうから、この記事の留意点だけで行けそうな気もしています。
※ 12/12/03 Booleanについては流石に考慮してあり、「stdBool.h」をincludeすると「bool」という関数属性が使えます。
- 関連記事
-
- XC8とMPASMの混在は無理そう・・・
- XC8とMPASMの混在が微妙・・・
- PIC用Cコンパイラ無償版の「char型の扱い」
- 寄る年波には勝てぬ・・・でもC言語は便利(^^;
- PIC16F拡張版の思い出し
コメントの投稿
プログラムかけるようになりたひ
PICのプログラミングやってみようと思って、PICの本買ってもなかなか^^;
DDS制御とかキーヤーなどのプログラムが自由に書けるようになりたいです...
DDS制御とかキーヤーなどのプログラムが自由に書けるようになりたいです...
独学でも・・・
ぶんさん
C言語なら、独学でもいけると思いますよ。是非、頑張って勉強してみて下さい。
やはり、簡単なプログラムを動かしてみるのが近道かな?
C言語なら、独学でもいけると思いますよ。是非、頑張って勉強してみて下さい。
やはり、簡単なプログラムを動かしてみるのが近道かな?