H8/3069RF マニュアルのメモ

4.1.2 節の割り込み発生時の動作な記述が以下との事。

  1. プログラムカウンタ(PC)とコンディションコードレジスタCCR)をスタックに退避
  2. CCRの割り込みマスクビットを1にセット
  3. 起動要因に対応するベクタアドレスを生成し、そのベクタアドレスの内容が示す番地からプログラムの実行が開始される

なんとなく常識的なカンジ。
で、4.5 節で stack の状態が図示されてます。これも成程ッスね的ナニ。
ってことでこれを踏まえてソースの差分を盛り込んで読んでみます。

ソースの中身確認

main 手続きから掘削開始。とりあえず init 手続きにおいて softvec_init という手続きの呼び出しが追加されている模様。

static int init(void)
{
  /* 以下はリンカ・スクリプトで定義してあるシンボル */
  extern int erodata, data_start, edata, bss_start, ebss;

  /*
   * データ領域とBSS領域を初期化する.この処理以降でないと,
   * グローバル変数が初期化されていないので注意.
   */
  memcpy(&data_start, &erodata, (long)&edata - (long)&data_start);
  memset(&bss_start, 0, (long)&ebss - (long)&bss_start);

  /* ソフトウエア・割り込みベクタを初期化する */
  softvec_init();

この関数は interrupt.c にて定義されています。

/* ソフトウエア・割込みベクタの初期化 */
int softvec_init(void)
{
  int type;
  for (type = 0; type < SOFTVEC_TYPE_NUM; type++)
    softvec_setintr(type, NULL);
  return 0;
}

/* ソフトウエア・割込みベクタの設定 */
int softvec_setintr(softvec_type_t type, softvec_handler_t handler)
{
  SOFTVECS[type] = handler;
  return 0;
}

SOFTVECS という配列を NULL で初期化しているのは分かるのですが、配列の定義やら softvec_handler_t とかの定義が面白い。主な場所としては interrupt.h となっております。まるっと引用。

/* 以下はリンカ・スクリプトで定義してあるシンボル */
extern char softvec;
#define SOFTVEC_ADDR (&softvec)

typedef short softvec_type_t;

typedef void (*softvec_handler_t)(softvec_type_t type, unsigned long sp);

#define SOFTVECS ((softvec_handler_t *)SOFTVEC_ADDR)

softvec はリンカスクリプトで定義とある。MEMORY コマンドな記述が以下。

MEMORY
{
	romall(rx)	: o = 0x000000, l = 0x080000 /* 512KB */
	vectors(r)	: o = 0x000000, l = 0x000100 /* top of ROM */
	rom(rx)		: o = 0x000100, l = 0x07ff00

	ramall(rwx)	: o = 0xffbf20, l = 0x004000 /* 16KB */
	softvec(rw)	: o = 0xffbf20, l = 0x000040 /* top of RAM */
	buffer(rwx)	: o = 0xffdf20, l = 0x001d00 /* 8KB */
	data(rwx)	: o = 0xfffc20, l = 0x000300
	stack(rw)	: o = 0xffff00, l = 0x000000 /* end of RAM */
}

RAM の先頭らしい。SECTION コマンドの一部が以下で

	.softvec : {
		_softvec = .;
	} > softvec

	.buffer : {
		_buffer_start = . ;
	} > buffer

上記の _softvec を外部参照しているはず。以下、記述の意図を順に列挙してみます。

  • _softvec のアドレスを SOFTVEC_ADDR で define
  • softvec_type_t 型を short で typedef
  • softvec_handler_t 型を void(*)(softvec_type_t, unsigned long) な関数ポインタとして typedef
  • SOFTVEC_ADDR は softvec_handler_t なポインタとして define

という事で _softvec は softvec_handler_t の配列の先頭アドレスということになっているはず。

ちなみに

main 手続きの init 手続き呼び出し直前に以下のマクロが記述されてます。

  INTR_DISABLE; /* 割込み無効にする */

  init();

これも interrupt.h にて定義があります。

#define INTR_ENABLE  asm volatile ("andc.b #0x3f,ccr")
#define INTR_DISABLE asm volatile ("orc.b #0xc0,ccr")

enable で ccr レジスタの上 2bits 0 に、disable で上 2bits 1 に、という事か。boot_loader では enable にしてないのだけれどおそらく問題無いはず。os/main.c に INTR_ENABLE な記述もあるみたい。

そーゆー意味では

bootload 側では初期化して割り込みを disable にしてるだけで後は何もしてないのかな。あ、違うや。割り込みベクタを設定してるな。

void (*vectors[])(void) = {
  start, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 
  intr_syscall, intr_softerr, intr_softerr, intr_softerr,
  NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  intr_serintr, intr_serintr, intr_serintr, intr_serintr,
  intr_serintr, intr_serintr, intr_serintr, intr_serintr,
  intr_serintr, intr_serintr, intr_serintr, intr_serintr,
};

配列を初期化してるシンボルは外部参照です。intr_* は intr.S で定義されてます。
例えば intr_softerr の定義は以下。

_intr_softerr:
	mov.l	er6,@-er7
	mov.l	er5,@-er7
	mov.l	er4,@-er7
	mov.l	er3,@-er7
	mov.l	er2,@-er7
	mov.l	er1,@-er7
	mov.l	er0,@-er7
	mov.l	er7,er1
	mov.w	#SOFTVEC_TYPE_SOFTERR,r0
	jsr	@_interrupt
	mov.l	@er7+,er0
	mov.l	@er7+,er1
	mov.l	@er7+,er2
	mov.l	@er7+,er3
	mov.l	@er7+,er4
	mov.l	@er7+,er5
	mov.l	@er7+,er6
	rte

している事は一箇所を除くと全て同じ r0 レジスタの設定のみ。基本的にしていることは

  • er[0-6] レジスタを stack に push
  • スタックポインタの値を er1 レジスタに格納
  • intr.h で定義されてる SOFTVEC_TYPE_* を割り込みの型毎で r0 に格納
  • interrupt 手続きに jmp
  • stack から er[0-6] レジスタに値を復帰
  • 戻る

ちなみに interrupt 手続きの定義は以下ですが

void interrupt(softvec_type_t type, unsigned long sp)
{
  softvec_handler_t handler = SOFTVECS[type];
  if (handler)
    handler(type, sp);
}

二引数の場合は r0、r1 という形で渡されるのですね。bootload 側はこの程度なのかどうか。確認できてる所では intr.S というソレは os/ 配下には無いので割り込みハンドラの動作を規定しているのは bootload 側、ってことになりますね。
む、そーゆー意味では bootload 側は serial な割り込みは使ってないけど、無理矢理 XMODEM で kozos なデータのやりとりはしてるのか。

とりあえず

エントリ投入します。os 側は別途とゆー事にて。