皆さん こんにちは。
この記事はインターフェース誌2023年7月号の「ゼロから作るOS」を参考にしています。
まずは書籍の本章を読んで予習しておくことをお薦めします。
私はまず110ページの<特設>を読んでマイコンの基礎を学んだ後、第1部から読み進めています。
読むだけでは頭に入らないので、今回からソースコードをIDEに取り入れて動作させてみることにしました。
ソースコードは
第2部3章、第2部4章、
第3部1章、第3部2章、第3部3章、
第4部1章、第4部2章、第4部3章、
第5部
の9パターンがあるようです。
前回の 環境構築編 では第2部3章のソースコードを取り込みましたので、その動作を見てみます。
デバッグ機能
まずIDE(eclipse)の使い方を少し確認してみます。
一度デバッグしたものは、Run – Debug History からリストが出てくるので、pico_tryknl Debug を選択します。
うまくいくと以下のように main()関数内でブレーク(停止)します。

ファンクションキーを押すか、以下のツールバーからアイコンを選択してデバッグします。
![]()
左から
Resume (F8キー):プログラムを停止することなく実行します
Suspend:プログラムを一時停止します
Terminate(Ctrl+F2キー):デバッグを中止します
Disconnect:切断します(選択できないので使い方が良くわかりません)
Step Into(F5キー):ステップ実行し、関数の場合には中に入っていきます
Step Over(F6キー):ステップ実行し、関数の場合には関数からリターンしてきます
Step Return(F7キー):実行中の関数をリターンするところまで進みます
最後の i→ のアイコンについて詳しく説明します。
押すと以下のように赤枠2か所のアイコンがへこんでInstruction Stepping Mode に入ります。

右側のペインには以下のようにDissasemblyのビューが表示され、アセンブラの命令でステップ実行できるモードになっています。

Step Into(F5キー):ステップ実行し、サブルーチンの場合には中に入っていきます
Step Over(F6キー):ステップ実行し、サブルーチンの場合にはリターンして戻ってきます
アセンブラをデバッグする場合には強力な助っ人機能ですね (^^)
それからブレークポイントを貼るにはソースコードの行番号付近をダブルクリックします。
すると行番号の左に〇印がつき、プログラムがそこに来ると停止します。
もう一度ダブルクリックすると〇が消えてブレークポイントが解除されます。
ビュー
Window – Shon View – Registers で汎用レジスタの値を表示することができます。
Nameの下にある>をクリックするとツリーが展開されて各レジスタの値を表示します。

Window – Shon View – Memory で指定した番地のメモリーを表示することができます。
eclipse を触り慣れていない方は、いろいろと試してみてください。
(私もあまり詳しくないので、操作説明はこのへんにしておきます)
インライン・アセンブラ
このプロジェクトではアセンブラの理解が必須です。
全てのコマンドを覚える必要はありませんが、出てきたものは全て調べて消化していくのが良いと思います。
幸いにもSTマイクロ社からCortex-M0+アセンブラのプログラミング・マニュアル(日本語版)が出ています。
こちら からダウンロードしておきましょう。
第2部3章ではまだOSのコードが出てきませんので、手始めにインライン・アセンブラのコードを見ていきます。
プログラムを実行中の場合、赤い四角のTerminateを押してデバッグを中止します。
Project Explorer で boot – reset_hdr.c をダブルクリックするとファイルが開き Reset_Handler()関数の付近が表示されます。

この中の DI(), EI()の関数内にインライン・アセンブラの記述があります。
DIの部分をマウスで左クリックしてカーソルを表示させて、右クリックして出てくるメニューの Open Declaration を選択(またはF3キーを押下)します。
以下の定義が表示されます。
/* 割込み禁止マクロ */
#define DI(intsts) (intsts=get_primask(), set_primask(1))
このすぐ上に以下のコードが書かれています。
/* PRIMASKレジスタ制御インライン関数 */
static inline void set_primask( INT pm )
{
__asm__ volatile("msr primask, %0":: "r"(pm));
}
static inline UW get_primask( void )
{
UW pm;
__asm__ volatile("mrs %0, primask": "=r"(pm));
return pm;
}
なにやら難しそうな文字が並んでいます。
__asm__から始まる部分がインライン・アセンブラです。
この形式は拡張アセンブリ構文というものです。
改行して表現もできるので、改行した形では以下の書式になります。
__asm__ ( アセンブリテンプレート
: 出力オペランドのリスト /* オプション */
: 入力オペランドのリスト /* オプション */
: 破壊されるレジスタのリスト /* オプション */
);
volatile は最適化を防止する修飾子で、思わぬ挙動になることを防いでくれます。
まずget_primask()内のインライン・アセンブラを見ていきます。
__asm__ volatile("mrs %0, primask": "=r"(pm));
改行を含んだ形にすると以下のようになります。
__asm__ volatile("mrs %0, primask"
: "=r"(pm)
);
後に続くリストは何もなければ省略することができます。
この場合は、アセンブリテンプレートの後に出力オペランドのリストだけが続いています。
アセンブリテンプレート : “mrs %0, primask”
出力オペンランドのリスト : “=r”(pm)
となっていて、入力オペランドと破壊されるレジスタのリストは省略されています。
アセンブリテンプレートにはアセンブラを記述し、%0の部分は “=r” によりコンパイラが自動的にレジスタを割り当ててくれます。
出力オペランドの場合、rの手前に=をつけます。
従ってこの例の場合、mrs Rd, primask というアセンブラが実行されて(Rdはコンパイラが割り当てるレジスタ)、その値がC言語の変数pmに代入されます。
mrs という命令は特殊なレジスタの値を汎用レジスタに取り込む命令です。
get_primask()はpmを戻り値として返します。
入力無し、出力ありなのでインライン・アセンブラの形式とつじつまは合っています。
次にset_primask()です。
__asm__ volatile("msr primask, %0":: "r"(pm));
改行を含んだ形にすると以下のようになります。
__asm__ volatile("msr primask, %0"
:
: "r"(pm)
);
後に続くリストは何もなければ省略することができます。
この場合は、アセンブリテンプレートの後に出力オペランドのリストが続き、その後に入力オペンランドのリストが続いています。
出力はないのですが入力の位置を示すために、出力の部分はコロン(:)だけを記述する必要があるわけです。
アセンブリテンプレート : “msr primask, %0”
出力オペランドのリスト : なし
入力オペランドのリスト : “r”(pm)
となっていてと破壊されるレジスタのリストは省略されています。
アセンブリテンプレートにはアセンブラを記述し、%0の部分は “r” によりコンパイラが自動的にレジスタを割り当ててくれます。
入力オペランドの場合、rの手前には何もつけません。
従ってこの例の場合、msr primask, Rn というアセンブラが実行されて(Rnはコンパイラが割り当てるレジスタ)、pmの値がRn経由でprimaskに代入されます。
msr という滅入れは汎用レジスタの値を特殊なレジスタに取り込む命令です。
set_primask()はpmを入力とします。
入力あり、出力なしなのでインライン・アセンブラの形式とつじつまは合っています。
なお primask は特殊なレジスタで他のペリフェラル・レジスタのようにメモリー・マップト(構造体として定義)されていません。
ですから msr/mrs 命令を使ってアクセスする必要があります。
今回はデバッグ機能とインライン・アセンブラについて説明しました。
第2部3章ではプログラムを実行してLチカできれば成功とします。
うまく動作できたでしょうか?
お疲れさまでした。
この記事のつづきは こちら です。