コンパイラ化するための方法として 独自言語の検討(その2)では一足飛びに PIC24 のマシン語を出力するのはディバッグが大変なので、一旦簡易バイトコード出力し、VM上で動作させた上でバイトコード出力部を PIC24 のマシン生成処理に変えるというように二段階の方がいいのかもしれないというようなことを書きました。
前者のディバッグが大変と考えた大きな理由の1つは、現状の私の環境では実機上でステップ動作ができない(MPLABXではpickit2でPIC24のステップ動作ができないorz)からです。
ステップ動作ができないのでディバッグ効率がかなり悪い(特にマシン語)のです。 OneBitLoader(ブートローダー)もほぼ机上ディバッグで作りましたがプログラムステップが短かったのでなんとかなりました。
今回は、pickit3 を購入する(まぁあまり高いものでもないし)ことでディバッグ環境を改善し、一足飛びに PIC24 のマシン語を出力することにします。(ステップ動作ができれば問題ありません^^)。
昔はZ80でハンドアセンブルしていたので GAME80 コンパイラのソースを見ていると生成しているマシン語が判りますが、PIC24FJ では流石にそういうわけには行かないのでコンパイラ化するためにはアセンブラのサブセット機能が必要になります。
インラインアセンブラを使ってもいいのですが、引数の渡し方が使いづらかったこととコンパイラで使用する命令は数が限られているのでインラインアセンブラは使用しないことにしました。
そこで次のような方法でコード生成処理を作る予定です。
また、当初は1パスのコンパイラにする予定でしたが、Flashメモリの書込み回数を減らすためエラーチェックでパスしたらFlashメモリへ書込む2パス方式にします。
- プリプロセッサのマクロで簡易アセンブラを実現する。
Cコンパイラのマクロでアセンブリ命令を定義します。ニーモニックは名前が重ならないように適当に変えます。
例 MOV_lit16_Wd(lit16,reg)のみ実装した状態
使用例:MOV_lit16_Wd(0x1234,W(2))
#define ASSEM #define W(n) (n) // n=0..15 #ifdef ASSEM #define DISP(fmt,p0,p1,p2) printf("\n%04x : ",Flash);printf(fmt,p0,p1,p2); #else #define DISP(fmt,p0,p1,p2) #endif #define FWRITE( code ) FlashWrite( Flash, code ) #define COND if( Pass == 0 ) // アセンブリ命令 #define MOV_lit16_Wd(lit16,reg) COND {DISP("MOV #%d,W%d",lit16,reg,0);} else \ {FWRITE(0x200000|(lit16<<4)|reg);} Flash+=2;
- picleソースのインタープリット部をマシン語生成処理に変える。
まずは1パス目で出力されるニーモニックを確認し、picleソースの内容を実現できているか机上確認する。
- 生成されたコードが1パス目で表示されたニーモニックとあっているかを確認する。
意味的には簡易アセンブラ機能の動作確認ということになります。
- フラッシュ上での確認
フラッシュメモリ上に生成されたマシン語をステップ動作でpicleソース通りの動作をしているか確認する。
- 最後に1の「#define ASSEM」行をコメントアウトしリコンパイルする。
コメントアウトすることでニーモニック出力部分が削除され、かつ printf() もリンクされなくなるのでコンパイル後のサイズが小さくなる。