091113

|


H8/300H(3052), H8/300(3664)にも継続を使えるスレッドを実装しました。H8は
バス幅が16bitなので16bitCPUなのだけど、レジスタは32bitある。アドレス空
間はH8/300は16bit、H8/300Hは24bitと、レジスタ長もバス幅もアドレス空間も
異なるという挑戦的なCPUなのだ。

そして、このCPUの場合、ARM,SHのようなリンクレジスタはなく、スタックにそ れを退避する。そしてステータスレジスタ(CCR)もスタックに退避される。 それは32bitの領域に退避されて、
|CCR|   |adr|adr| ... H8/300
|CCR|adr|adr|adr| ... H8/300H
のようになる。通常のサブルーチンのリターン命令(rts)ではアドレス部分だけで リターンし、例外からのリターン命令(rte)ではこのステータスレジスタの内容も 復帰してリターンする。
なのでスタックを放棄したり、つけかえたりする場合には、そのスタックにこ れらを設定するということが必要になる。
#ifdef __NORMAL_MODE__
#define	ADDRESS_MASK	0x0000ffff	// 16bit address
#else
#define	ADDRESS_MASK	0x00ffffff	// 24bit address
#endif
#endif

thread_t
md_thread_create (thread_t tc, thread_func_t start, uint32_t arg)
{

  tc->regs.er0 = arg; // pass 1st arg.
  // install return address for 'rte'

既にスタックが割りあてられているのであれば、そのスタックにリターンアドレス
を設定します。これは'RTE'命令で戻るので、割り込み禁止で走るように設定。
  if (tc->tc_stack)
    *(uint32_t *)tc->tc_stack->bottom =
      (addr_t)start | 0x80000000; // disable interrupt.
  else
    tc->continuation = (continuation_func_t)start;
まだスタックがない場合は、スタート場所を継続として設定。これはいずれ
スタックが割りあてられた時に、使われる。

  return tc;
}

void
md_thread_stack_discard (thread_t tc)
{

  tc->regs.sp = 0;
}

void
md_thread_continuation_setup (thread_t tc)
{
  // Setup register set for the next context switch.
  // Already stack is attached.
  tc->regs.sp = (addr_t)tc->tc_stack->bottom;

  if (tc->continuation)
    {
ここで、はじめて設定されたスタックにリターンアドレスとステータスレジスタ
を設定します。このtc->regs.ccrはdo_thread_switchで保存されたステータス
レジスタ。
しかしこれは冗長。割り込みから中断されたコンテキストなら、継続はないの
でそもそもここは呼ばれない。ここが呼ばれるということは明示的に
do_thread_switch を呼んだということなので、常にそのステータスレジスタは
割込み禁止。
ここは、単に割り込み禁止のフラグを立てるだけでいい。はず。それはつまり
継続の一番最初は割込み禁止で入ってくるということ。
      // [CCR | return address] preserve control register.
      assert (!(tc->regs.ccr & ADDRESS_MASK));
      assert (!((addr_t)tc->continuation & ~ADDRESS_MASK));
      *(uint32_t *)tc->tc_stack->bottom = tc->regs.ccr |(addr_t)tc->continuation;
      tc->continuation = NULL;	// scheduled now.
    }
}

ここ、継続の引数なし版と、スレッドの引数あり版の二つにしたいとも思っている。
void
md_thread_continuation_call (continuation_func_t cont)
{
  // Rewind stack and call continuation.
  current_thread->continuation = NULL;	// dispatch here.
  __asm volatile ("mov %0, sp \n jmp @%1"::
		  "r"(current_thread->tc_stack->bottom), "r"(cont));
  // NOTREACHED
}
実際はこの程度でよかったのだけど、久々のH8、そしてH8はメモリがない。それで メモリ破壊バグをやらかしたりして、絶望的に時間がかかってしまった。
メモリ破壊例。
000fdfc B _uart_recv
0000fe88 B _uart_send
0000ff14 B _app_tc
0000ff60 B ___sys_flag
0000ff62 A _bss_end	いくらなんでもスタックがなさ過ぎ! BSS破壊していた。
0000ff80 A _stack_start

これでなんとか。
000fc7a B ___st_recv
0000fc7c B _uart_recv
0000fd08 B _uart_send
0000fd94 B _app_tc
0000fde0 B ___sys_flag
0000fde2 A _bss_end
0000ff80 A _stack_start
メモリ2KBはつらい。
あと、まだx86が残っている。これも同じようにスタックに退避されているのだけど、 セグメントレジスタとフラグレジスタも復帰するので合計12byteのスタック領域を 設定しないといけないんだ。