PCCの構成は http://pcc.ludd.ltu.se/ftp/pub/pcc-docs/porttour.ps に移植方法は http://pcc.ludd.ltu.se/ftp/pub/pcc-docs/targdocs.txt に分かりやすいドキュメントがあります．拡張子.psのドキュメントはPostScript形式です．オンラインサービス http://www.ps2pdf.com などでPDF形式に変換できます．

フロントエンドおよびバックエンドそれぞれの移植方法を書いていきます．

PCCのコードは pcc-08613.tgz を使用しました．また移植したコードは http://febhare.bake-neko.net/Src/pcc_porting_diff.tgz です． 移植したコードを走らせるには，pccのコードを展開した後に，上記の移植コードを上書きでコピーしたのちにMakeします． pccは厳密に同じ日付ではなくても コンパイル できると思います． コマンドは以下のとおりです:

pccはビルドにThe GNU configure and build systemを利用しています．まずは移植用コードをビルドできるようにします． まずプロセッサ名をSimple Risc Processor の頭文字をとりsrpとします．移植コードは "arch/srp"以下に置くとします． サポートするOSは NetBSD とします． まずconfigure.acの20行目あたりを以下のように編集します．ターゲットCPUがsrpのときにtargmachにsrpを設定します．

のようにビット幅を定義します．アライメントも同様です． レジスタ の定義は，クラス， レジスタ の状態，そして重なりをそれぞれ指定します． レジスタ は，例えば8-bit幅と16-bit幅の レジスタ ， 浮動小数 点用の レジスタ と，いくつかのクラスに分けます． それらのクラスは，RSTATUSにSAREGのようにSxREGの形で定義します．クラスはAからDまでの4通りが取れます． 今回のターゲット SPR の0番の レジスタ は常に0の定数なので， レジスタ 割り当てに使いません．そのような レジスタ は0を設定します． レジスタ が関数呼び出し側で保存されるならTEMPREG，被呼び出し側で保存されるならPERMREGとします． 最後に レジスタ の重なりを指定します．SPRはそうではないのですが，ターゲットによっては例えば16-bit レジスタ を2本の8-bit レジスタ として使うことができます．このような レジスタ の重なりをROVERLAPに指定します．これは重なり合う レジスタ を配列で指定します．

フロントエンドは，"arch/srp/local.c" と "arch/srp/code.c" から構成されます．解析した ソースコード から構築した解析木をバックエンドに送る前に処理をすることができます．ここで機械依存の処理をすることはないので，他の適当な アーキテクチャ からファイルをコピーしてくればよいです．

バックエンドの移植

"arch/srp/local2.c", "arch/srp/table.c", および"arch/srp/order.c"を書きます．まず中間言語を機械語に変換するルールを記述するtable.cを説明します．

table.c にはstruct optab の配列 table[]を書きます．struct optab は"mip/pass2.h"で定義される下記の構造体です．

extern struct optab { int op; int visit; int lshape; int ltype; int rshape; int rtype; int needs; int rewrite; char *cstring; } table[];

この構造体に，例えば加算ならば下記のように値を設定します．

{ PLUS, INAREG, SAREG, TINT, SAREG, TINT, NAREG|NASL, RESC1, " add A1, AL, AR ; plus

" , },

イメージとして，ツリー構造の中間言語の節にあてはまるstruct optabをそれぞれ適用して，その都度機械語に変換していきます．

struct optab のフィールドopにはマッチするノードタイプを設定します．ノードタイプは"mip/node.h" 109行目から定義されています．演算"+"を意味するノートタイプPLUSを指定しています．

struct optab のフィールドvisitには，このルールを適用する場面をかく(みたい?)です．コードを生成するときに，このフィールドのビットが立っているかどうかで，ルールの適用を行っているみたいです．例えば 条件分岐の機械語を生成するときは FORCC が立っている物を使うようです．よく理解していないのですが，結果をレジスタに収めるものはINAREG (クラスAのレジスタ?)を指定しています．

struct optabのフィールドlshapeおよびltypeは，変数の型などを指定します．lxxは木の左ノード，rshape, rtypeは木の右ノードを示します．shapeはpass2.hの73行目から定義されていて，レジスタクラス SxREG ，コンスタントSCON，レジスタ間接アドレッシングSORえｇなどが設定できます．typeはpass2.hの112行目から定義されており，C言語の変数型が例えばTCHARのように設定できます．

フィールド needsは，このルールを適用するのに必要なレジスタなどを指定します．なにも必要ないなら0を設定します．例えばCLASS A のレジスタが2つ必要ならば 2*NAREGとします．さらにこの2つのレジスタを，ツリーの左要素のレジスタと共有してよいならばNASL(ツリーの右要素と共有するなら NASR)を追加します．ここで確保したレジスタは，最後の機械語生成で，例えばクラスAのレジスタならばA1, A2のように指定できます．

フィールドrewriteは，このルールの結果をどこに保存されるかを設定します．確保したレジスタ(先ほどの例でいえばA1)に収められるならRESC1 (同様にA2ならばRESC2)を設定します．

下記のような単なる型変換ならば，RLEFT/RRIGHTのように木の子要素をそのまま指定します．下記の例ではTCHARのノードをTSWORDもしくはTSHORTの型に変換します．

{ SCONV, INAREG, SAREG, TCHAR, SAREG, TSWORD|TSHORT, 0 , RLEFT, " # convert char to short/int

" , },

代入を行うASSIGNなどは，rewriteにRDESTを指定しなければいけません．(文法チェックにそう書かれている.)

最後のcstringは変換結果を文字列で指定します．アルファベット小文字はそのまま出力されます．アルファベット大文字のものは以下のルールで置換されます．

この置換は mip/match.cの 265行目の関数expand()が行います．使うのは'A'のアドレス指定だと思います．関数expand()は'A'で始まる要素を見つけると，local2.c にある関数adrput()を呼び出します．この関数adrput()は自分で実装します．また，'Z'で始まるものは"spr/arch/local2.c"に記述する関数zzzcode()を呼び出しますから，ターゲット固有の特別な変換をしたいときに頭文字Zで始めておけば便利かもしれません．

2番目の文字が，'L' , 'R'ならば木の左/右要素を示します．数字'1'~'4'ならば，例えばフィールドneedsで要求したレジスタが割り当てられます．