しゅうのマイクロマウス研修 キット製作研修

番外編 Part5 書き込む準備、プログラムの用意!1 – しゅうのマイクロマウス研修

しゅうのマイクロマウス研修

こんにちは、しゅうです。
前回は書き込むためのツールを用意しました。あとはその時に書き込むプログラムを用意していくだけです。
CS+で使っていた時のプログラムは、そのまま丸ごと使うことはできません。しかし、いくつかは少し編集を加えるだけで流用することが可能です。今回は必要なプログラムとその解説を少し行います。

何が必要?

基本的な構造はCS+の時とそこまで変わりませんが、今回用意するプログラムは次のようなものです。

ファイル 詳細
マイコン用の環境ファイル マイコンごとに用意されるものです
スタートアップファイル マイコンを立ち上げて最初に行う一連の動作
各種レジスタに関する設定ファイル 入出力レジスタの設定など
ベクタファイル 実行するプログラムなどをどこに置くかを指定するもの
こちらを参考にすると良いでしょう
割り込みに関する設定ファイル 実行したい割り込み処理が書かれている
main関数が書かれたファイルなど 実際の挙動に関わってくるもの
makefile ビルドするときの仕様書

まずは「マイコン用の環境設定ファイル」から準備していきます。

マイコン用の環境設定ファイル

GCC環境を構築した時に、RX-ToolChain/prefix/rx-elf/lib/rx.ldという環境設定ファイルが生成されています。これを複製して、R5F5631M.ldというファイル名に変更して保存しましょう。保存場所は書き込むプログラムをまとめているところにしましょう(workspace/mouse_program/srcなど)。
このままでは使用できないのでRX631用に書き換えていきます。まずは、MEMORYの中身を編集します。中身を次の様に置き換えましょう。オリジナルのままだと、メモリが大きすぎたりします。

RAM (w)	: ORIGIN = 0x00000000, LENGTH = 0x0000FC00 /* 64k - 1k */
USTACK (w)	: ORIGIN = 0x0000FD00, LENGTH = 4 /* ustack 0x100 bytes */
ISTACK (w)	: ORIGIN = 0x00010000, LENGTH = 4 /* istack 0x3000 bytes */
ROM (w)	: ORIGIN = 0xFFFC0000, LENGTH = 00003FFD0 /* 64k - 0x30(.fvectors) */

これらはRX631のハードウェアマニュアルに記載されており、RAMのサイズは64k、ROMのサイズは256kです。オリジナルはSTACKのみですが、ユーザスタック(ustack)と割り込みスタック(istack)に分けています。これはCS+の時からの名残りです。そういえばCS+を使っていたときは、無償利用だったのでコンパイルのサイズ制限(128k)がありましたが、今回のGCC環境ではそれがないのでマイコンのROMを全て使うことができますね。
次のSECTIONSの中身では、最後の方にあるKEEP (\*(.init))とKEEP (\*(.fini))はCS+の環境のためで今回は使いませんので消しちゃいます。これについで、.initや.finiの部分も消しましょう。
次は.stackのところを.istackと.ustackに置き換えます。

  .istack (ORIGIN (ISTACK)) :
  {
    PROVIDE (__istack = .);
    *(.istack)
  }

  .ustack (ORIGIN (USTACK)) :
  {
    PROVIDE (__ustack = .);
    *(.ustack)
  }

そして、オリジナルではこの後に続くPROVIDEの部分から.vectorsまでは、ベクタを予め定義しています。今回は別のファイルで定義を行うのでまるっと次の様に置き換えましょう。

  .fvectors 0xFFFFFFD0 : {
		build/vecttbl.o(.fvectors)
  }

  .rvectors  : {
		build/vecttbl.o(.rvectors)
  } > ROM

fvectorsが固定ベクタのことで、開始アドレスが決まっており0xFFFFFFD0と直接アドレスを記述しています。一方でrvectorsは可変ベクタのことで開始アドレスが定まっておりません。さらに、> ROMの指示によってROM上の何処かに配置されます。これにより、配置された後にvecttbl.cに書かれているRelocatable_Vectorsの配列のアドレスが決まります。そこからスタートアップファイルで書かれているレジスタがセットされていくという仕組みになっています。

次はそのスタートアップファイルを記述・解説していきます。

スタートアップファイル

このファイルは、MAIN関数を実行する前に実行されます。CS+で開発していたときは、MAIN関数を実行する前にresetprg.cというファイルを実行していました。こちらの資料の第8章「8.スタートアップ」の部分を読むと一連の流れが把握できます。
resetprg.cでは具体的に、PowerOn_Reset_PC(void)関数を実行しており、その中身は次に書かれている処理を行っています。

set_intb(__sectop("C$VECT"));
set_fpsw(FPSW_init | _ROUND | _DENOM);
_INITSCT();
set_psw(PSW_init);
main();
brk();

ここで実行されている、set_intb、set_fpsw、_INITSCT()やset_pswはCS+が用意している組み込み関数の1つです。こちらにCS+が提供している組み込み関数の一覧が表示されています。そしてこれらを再現するためにはアセンブリ言語で書く必要があります。僕は、アセンブリを書くのは今回が初めてですが、先輩曰く難しいことはやらないしアセンブリ言語を使うのはこのスタートアップファイルのみです。頑張って理解していきましょう・・・!まずは先ほどのPowerOn_Reset_PC(void)関数が何をしているかを簡単にまとめると以下の様になります。

  1. 可変ベクタの先頭アドレスを設定
  2. 浮動小数点ステータスワードレジスタの値を書き換える
  3. 静的変数の初期化
  4. 割り込み処理の許可
  5. メイン関数を呼び出す

実際にset_intb()をアセンブリ言語で書くと次のようになります。

mvtc #_Relocatable_Vectors, intb

ここで使用しているmvtcはMoVe To Control registerから由来しており、制御レジスタへの転送を行うコマンドになります。このコマンドはmvtc src, destとなっていた場合、dest(デスティネーションオペランド)にsrc(ソースオペランド)を転送します。先程の1行だとINTBレジスタに#_Relocatable_Vectorsを転送しております。この変数みたいな#_Relocatable_Vectorsは別のファイルで定義しているので、次のようにファイルの最初の方に外部にあることを教えてあげる必要があります(ヘッダファイルをインクルードする感じと似てますね)。

.extern _Relocatable_Vectors

これでINTBの設定はできました。そして、mvtcコマンドを使って同様にFPSWとPSWレジスタも設定できます。コマンドの詳細なお話しやどの制御レジスタに転送可能かはこちらに書かれています。

#set_fpsw()
mvtc #0x100, fpsw

#set_psw()
mvtc #0x10000, psw

そして最後に_INITSCTについて書きます。メモリを初期化しますが、C言語と違って電源を入れた時のRAMの値が不逞なため、しっかりRAM全体を0と定義する必要があります。次の一連の記述で行えます。

mov #0, r1
mov #0, r2
mov #__istack, r3
sub r1, r3
sstr.b

ここで使用しているmovコマンドはmov src, destと使い、srcの値をdestに転送します。つまりr1とr2には0、r3には割り込みスタックポインタ(ISP)である#__istackを転送しています。これは先程の#_Relocatable_Vectorsと同様に外部で定義されています。その後のsubコマンドはsub src, destと使い、destからsrcの値を引きます(C言語的に書くとdest = dest – srcとなりますね)。そして最後のsstr.b命令によって、今回はr1からr3までの領域をr2で埋めていきます。具体的には、今回使用するRX631のRAMのアドレスが64kbyteの場合、0x0000_0000から0x0000_FFFFとなりますので、その間を全て0にして初期化する、と言うことになります。

以上の一連の流れと、前にrxprogをgit cloneしたリポジトリにあったstart.sを参考にすると、今回作りたいスタートアップファイルは次のようになります。

    .text
    .global _start
    .global _exit
    .global _PowerON_Reset_PC
    .extern _Relocatable_Vectors
    .extern __ustack #ユーザースタックポインタ
    .extern __istack #割り込みスタックポインタ

_PowerON_Reset_PC:
_start:
    mvtc #__istack, isp
    mvtc #__ustack, usp
    mvtc #_Relocatable_Vectors, intb
#set_fpsw()
    mvtc #0x100, fpsw
# RAM 全領域のゼロクリア
    mov #0, r1
    mov #0, r2
    mov #__istack, r3
    sub r1, r3
    sstr.b
#.bss セクションの消去
    mov	#__bssstart, r1
    mov	#0, r2
    mov	#__bssend, r3
    sub r1, r3
    sstr.b
#初期値付き変数の初期化
    mov	#__datastart, r1
    mov	#__romdatastart, r2
    mov	#__romdatacopysize, r3
    smovf
#set_psw()
    mvtc #0x10000, psw
#mainへジャンプする
    mov.l #0,r1 ; argc
    mov.l #0,r2 ; argv
    bra	_main

_exit:
    wait
    bra _exit

参考にしたstart.sファイルには、今回ご紹介しなかった.bssセクションの消去やROMの初期化も含まれていました。また、set_psw()を実行したら、bra _mainなどでメイン関数を呼び出すようにもしてあります。

まとめ

長くなったので、今回はここまで。RX631の環境設定ファイルとスタートアップファイルを書き終えました。次回は、あと必要なファイルに関する解説などを行っていきます。

タイトルとURLをコピーしました