特集＊これならわかる！ マイコン入門
第 9 章 スタートアップ・ルーチンのしくみとスタック・フレームの役割を理解しよう

C プログラムの裏側を探ってみよう！
山本 秀樹
Hideki Yamamoto

マイコンのプログラム開発では，大半の場合 C 言語が使われていると思います．C 言語はマイコンの違い

リセットされると，ラベル start から実行が始まります．まずスタック・ポインタの初期化，マイコンをある程度隠蔽することができ，プログラマから見るのプロセッサ・モードの設定，可変ベクタ・テーブルと使いやすい言語です．
さらにアセンブリ言語の知識があれば，C 言語で書の設定を行います．
その後は，マクロ・アセンブラ機能が使われている

いたプログラムが実際にはどのように実行されているのかを理解することができ，よりよいプログラムの開ので，どのような命令が使われているのかわかりにくいのですが，初期値のない外部変数領域を 0 でクリア発に役立つのではないかと思います．
この章では，C 言語で作成したプログラムの裏側を，し，初期値のある外部変数領域に ROM 領域から初期値をコピーします．

アセンブリ言語の知識を使って眺めてみます．

次に，JSR 命令で main 関数を呼び出します．

スタートアップ・ルーチンの役割最後に，通常は main 関数の呼び出しから戻らないようにプログラムを作るのですが，もし main 関数のC 言語のプログラムでは，main 関数内の最初の行呼び出しから戻った場合，無限ループに入って暴走を防ぐようになっています．

を実行する時点ですでに，初期値のない外部変数は値

このスタートアップ・ルーチンで行っているのは，が 0 になり，初期値のある外部変数はその値で初期化されています．この操作はいつの間に行われたのでし最小限の処理です．これ以外に，例えばクロックを，リセット時に自動的に選択される低速クロックの 8 分ょうか．
アセンブリ言語で書いたプログラムでは，電源を投周から，もっと高速のクロックに切り替える処理や，入出力ポートや他の内蔵モジュールの初期化処理を行入してリセット・ベクタに書いたメモリにある命令がいたいのではないかと思います．そのような処理を C呼び出された時点では，RAM にどのような値が書かれているのかを保証できません．しかし，外部変数の言語の main 関数の最初で行うこともできますし，このスタートアップ・ルーチン内で行うように書き換え値は，RAM 上に書かれているはずです．このことから，マイコンがリセットされてから main 関数が呼びて，C 言語のプログラムからはそのような処理を一掃してしまうこともできます．

出されるまでの間に，C 言語のプログラムを実行するために必要な処理が行われたことがわかります．
この処理を行うプログラムは，スタートアップ・ルスタック・フレームの役割ーチン（または C ランタイム・ルーチン）と呼ばれます．
HEW で C 言語プログラムを作成した場合，デフォルC 言語の関数は，通常はサブルーチンとして実現されます．関数呼び出し，引数の受け渡し，関数内の自トでは ncrt0.a30 というファイルが用意され，ここにスタートアップ・ルーチンが書かれています．この動変数といった，C 言語でプログラムを作成するときには当たり前のように使っている機能について，どの内容をざっと見てみましょう．リスト 1 に，自動生成ように実現されているのかを見てみます．

されたスタートアップ・ルーチンの抜粋を示します．

HEW でリスト 2 のプログラムをビルドして，付録

Keywords
C 言語，スタートアップ・ルーチン，C ランタイム・ルーチン，スタック，フレーム，volatile，ncrt0.a30，スタック・ポインタ，プロセッサ・モード，可変ベクタ・テーブル，マクロ・アセンブラ，JSR 命令，ENTER 命令

2005 年 4 月号

203

リスト 1 スタートアップ・ルーチン ncrt0.a30（抜粋）;----------------------------------------------------------;----------------------------------------------------------; for R8C/Tiny;----------------------------------------------------------; initialize data section;---------------------------------------------------------N_BCOPY data_SEI_top,data_SE_top,data_SE

start:

N_BCOPY data_SOI_top,data_SO_top,data_SO;---------------------------------------------------------; after reset,this program will start

N_BCOPY data_NEI_top,data_NE_top,data_NE
N_BCOPY data_NOI_top,data_NO_top,data_NO;---------------------------------------------------------ldc
mov.b#istack_top,#02h,0ah

isp;set istack pointermov.b#00h,04h

mov.b#00h,0ah

ldc
ldc#0080h, flg#stack_top,

sp;set stack pointerldc#data_SE_top,

sb;set sb register

ldintb#VECTOR_ADR;set processer modeldc

#stack_top,sp;==========================================================; Call main() function;---------------------------------------------------------ldc#0h,fb.glb_main

jsr.a_main; for debuger

main関数の呼び出し;==========================================================; NEAR area initialize.

.endif ; __R8C__;---------------------------------------------------------; bss zero clear;----------------------------------------------------------;==========================================================; exit() function

N_BZERO bss_SE_top,bss_SE
N_BZERO bss_SO_top,bss_SO
N_BZERO bss_NE_top,bss_NE;----------------------------------------------------------

N_BZERO bss_NO_top,bss_NO_exit:

.glb
.glb

_exit
$exit
; End program$exit:
jmp_exit

マイコンにダウンロードしてみました．図 1 に，ダウ

このふるまいは，コンパイラの実装に依存します．

ンロードしたプログラムを示します．この画面は，HEW のエディタ画面のモードを，混合モードに切り

次に，JSR 命令により f 関数をサブルーチンとして呼び出しています．

替えることで得られます．なお，本特集では C 言語でプログラムを作成する方法について説明していません

呼び出された関数では， ENTER 命令によりスタック上に自動変数領域を作っています．この領域は，配が，ご了承ください．

列 buf の領域として使われます．

関数 f は四つの引数をとります．逆アセンブル結果を見ると，main 関数では第 4 引数からスタックに格

この結果からわかるように，スタックはサブルーチンの戻り番地やレジスタの一時待避だけに使われるの納しています．ここで使っているコンパイラでは，第1，第 2 引数をレジスタ R1 ， R2 に格納し，それ以降ではありません．関数呼び出しでは，引数，戻り番地，レジスタ待避，自動変数領域が，ある決まった構造での引数は最後の引数からスタックに格納するようです．

スタック上にとられます．このようにスタック上にあ

リスト 2 スタック・フレームを確認するプログラム

る決まった構造の領域が取られたものは，スタック・フレームと呼ばれます．

/* トランジスタ技術 2005年4月号 *//* 第9章 スタックフレーム */スタック上の自動変数領域や引数の領域には，CPU の FB レジスタを使ってアクセスしています ．

int f(int, int, int, int);

FB レジスタはスタック・フレームの基準点を指すレジスタとして使われます．

void main(void){
int x;

コンパイラの最適化による意図しない動作を抑止する volatile 宣言

x = f(0, 10, 1, 5);}
int f(int a1, int a2, int a3, int a4) {int
buf[10];inti;
for (i = a1; i < a2; i += a3)buf[i] = a4;
return a1+a2;}

204C 言語で作成したプログラムをコンパイルするとき，通常は最適化が行われます．この最適化はプログラム実行速度を向上したり，プログラム・サイズを小さくするのに役立ちます．しかし，マイコンのプログラムを開発する場合には，最適化によってソース・コードとは異なる実行プログラムになってしまうことがあり，注意が必要になります．
SFR を操作する場合がその典型例でしょう．ここ2005 年 4 月号